1
0
mirror of https://gitlab.com/comunic/comunicmobile synced 2025-04-05 12:19:21 +00:00

Compare commits

...

26 Commits

Author SHA1 Message Date
18cb8be655 Start to work on new version 2022-08-04 14:29:01 +02:00
c5ee235ebd Users can report their own generated content (thanks Google Play reviewers) 2022-08-04 13:45:10 +02:00
a24bb147c8 Update version plugin 2022-08-04 12:23:29 +02:00
ae56635564 Update flutter_webrtc plugin 2022-08-04 12:20:36 +02:00
c02f6f1deb Update firebase_messaging plugin 2022-08-04 12:14:52 +02:00
31fed62616 Update file_picker plugin 2022-08-04 12:07:03 +02:00
aff7e7434c flutter pub upgrade 2022-08-04 11:31:37 +02:00
f18b632ac8 Start to work on new version 2022-06-11 16:19:57 +02:00
b14eae6689 Update Gradle and target Android version 2022-06-11 15:15:38 +02:00
bae83430ab Update application dependencies 2022-06-11 15:04:11 +02:00
2c44793def Updated Flutter to v3 2022-06-11 14:12:44 +02:00
b86a456a03 Start to work on new version 2022-03-18 21:02:14 +01:00
e969c85188 Add missing french translations 2022-03-18 20:37:19 +01:00
de8b4f7fb4 Fix incorrect build number 2022-03-18 20:33:15 +01:00
f624971717 Remove report group icon on groups list if user is already a member of the group 2022-03-18 20:16:27 +01:00
49a1098a28 Can report group 2022-03-18 20:13:45 +01:00
64bbce2084 Can report user 2022-03-18 19:29:26 +01:00
504be2e5ef Close report dialog if user has already sent a report 2022-03-18 19:23:35 +01:00
79ed8e934e Can report conversation message 2022-03-18 19:21:08 +01:00
1bd7840be6 Can report conversation 2022-03-18 19:11:06 +01:00
e80232931e Can report comment 2022-03-18 19:05:21 +01:00
20b19d0a4a Can report post 2022-03-18 18:45:58 +01:00
ec16984b8a Managed to send reports 2022-03-18 18:29:25 +01:00
80a1c4e0c4 Start to build report dialog 2022-03-18 17:54:19 +01:00
512b058d34 Fix potential load issue on user page 2022-03-12 15:22:14 +01:00
73f20a543d Start to work on version 1.1.12 2022-03-12 11:15:26 +01:00
37 changed files with 806 additions and 288 deletions

View File

@ -34,7 +34,7 @@ if (keystorePropertiesFile.exists()) {
} }
android { android {
compileSdkVersion 32 compileSdkVersion flutter.compileSdkVersion
compileOptions { compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8 sourceCompatibility JavaVersion.VERSION_1_8
@ -59,8 +59,8 @@ android {
defaultConfig { defaultConfig {
applicationId "org.communiquons.comunic" applicationId "org.communiquons.comunic"
minSdkVersion 21 minSdkVersion 23
targetSdkVersion 32 targetSdkVersion flutter.targetSdkVersion
versionCode flutterVersionCode.toInteger() versionCode flutterVersionCode.toInteger()
versionName flutterVersionName versionName flutterVersionName
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

View File

@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip

View File

@ -340,6 +340,7 @@
"Failed to remove conversation logo!": "Erreur lors de la suppression du logo de la conversation !", "Failed to remove conversation logo!": "Erreur lors de la suppression du logo de la conversation !",
"Failed to remove member!": "Echec de la suppression d'un membre !", "Failed to remove member!": "Echec de la suppression d'un membre !",
"Failed to send a file!": "Erreur lors de l'envoi d'un fichier !", "Failed to send a file!": "Erreur lors de l'envoi d'un fichier !",
"Failed to send report!": "Erreur lors de l'envoi du signalement !",
"Failed to start recording!": "Erreur lors du lancement de l'enregistrement !", "Failed to start recording!": "Erreur lors du lancement de l'enregistrement !",
"Failed to toggle admin status of user!": "Echec du changement du status administrateur d'un membre !", "Failed to toggle admin status of user!": "Echec du changement du status administrateur d'un membre !",
"Failed to update conversation settings!": "Echec de la mise à jour des paramètres de la conversation !", "Failed to update conversation settings!": "Echec de la mise à jour des paramètres de la conversation !",
@ -500,6 +501,7 @@
"Please check back soon!": "Revenez plus tard finir la configuration de l'application !", "Please check back soon!": "Revenez plus tard finir la configuration de l'application !",
"Please choose new account image visibility level:": "Veuillez choisir un nouveau niveau de visibilité pour votre image de compte :", "Please choose new account image visibility level:": "Veuillez choisir un nouveau niveau de visibilité pour votre image de compte :",
"Please choose now the Forez group you want to join...": "Veuillez maintenant choisir le groupe #Forez que vous souhaitez rejoindre...", "Please choose now the Forez group you want to join...": "Veuillez maintenant choisir le groupe #Forez que vous souhaitez rejoindre...",
"Please choose the reason of your report:": "Veuillez indiquer la raison de votre signalement",
"Please click on the day you will be in the plain, so that everyone gets informed ! ;)": "Veuillez cliquer sur les jours où vous serez présent dans la plaine du Forez, que tout le monde soit au courant ;)", "Please click on the day you will be in the plain, so that everyone gets informed ! ;)": "Veuillez cliquer sur les jours où vous serez présent dans la plaine du Forez, que tout le monde soit au courant ;)",
"Please enter message content: ": "Veuillez entrer le contenu du message :", "Please enter message content: ": "Veuillez entrer le contenu du message :",
"Please enter new message content:": "Veuillez entrer le contenu du nouveau message :", "Please enter new message content:": "Veuillez entrer le contenu du nouveau message :",
@ -534,6 +536,7 @@
"Question 1": "Question 1", "Question 1": "Question 1",
"Question 2": "Question 2", "Question 2": "Question 2",
"Ready": "Prêt", "Ready": "Prêt",
"Reason of report": "Raison du signalement",
"Receive notifications for the conversations you follow.": "Recevoir des notifications push pour les conversations que vous suivez", "Receive notifications for the conversations you follow.": "Recevoir des notifications push pour les conversations que vous suivez",
"Record audio": "Faire un enregistrement audio", "Record audio": "Faire un enregistrement audio",
"Recording...": "Enregistrement...", "Recording...": "Enregistrement...",
@ -543,6 +546,8 @@
"Remove": "Supprimer", "Remove": "Supprimer",
"Remove selected image": "Supprimer l'image sélectionnée", "Remove selected image": "Supprimer l'image sélectionnée",
"Replace image": "Remplacer l'image", "Replace image": "Remplacer l'image",
"Report abuse": "Signaler un abus",
"Report successfully saved. Thank you for your contribution!": "Le report a bien été pris en compte. Merci pour votre contribution !",
"Request membership": "Demander de rejoindre le groupe", "Request membership": "Demander de rejoindre le groupe",
"Requested": "En attente", "Requested": "En attente",
"Respond to survey": "Répondre au sondage", "Respond to survey": "Répondre au sondage",
@ -642,8 +647,11 @@
"You can search the people you know and ask them to become your friends!": "Vous pouvez rechercher vos connaissances et les demander en amis !", "You can search the people you know and ask them to become your friends!": "Vous pouvez rechercher vos connaissances et les demander en amis !",
"You can search the people you know and ask them to become your friends!\\n\\nThis will help you to reach them to exchange information!": "Vous pouvez rechercher vos connaissances et les demander en amis, pour entrer facilement en contact avec elles !", "You can search the people you know and ask them to become your friends!\\n\\nThis will help you to reach them to exchange information!": "Vous pouvez rechercher vos connaissances et les demander en amis, pour entrer facilement en contact avec elles !",
"You can use this virtual directory.": "Vous pouvez utiliser ce répertoire virtuel.", "You can use this virtual directory.": "Vous pouvez utiliser ce répertoire virtuel.",
"You do not have any conversation yet!": "Vous n'avez pas encore de conversation !",
"You do not have any friend yet!": "Vous n'avez pas encore d'amis sur Comunic !",
"You do not have any notification now.": "Vous n'avez pas de notification pour l'intant.", "You do not have any notification now.": "Vous n'avez pas de notification pour l'intant.",
"You do not have any unread conversation yet...": "Vous n'avez aucune conversation non lue pour le moment...", "You do not have any unread conversation yet...": "Vous n'avez aucune conversation non lue pour le moment...",
"You have already sent a report for this resource!": "Vous avez déjà soumis un signalement pour cette ressource !",
"You must accept the Terms Of Service to continue.": "Vous devez accepter les Conditions d'utilisation pour continuer.", "You must accept the Terms Of Service to continue.": "Vous devez accepter les Conditions d'utilisation pour continuer.",
"You security questions have been successfully updated!": "Vos questions de sécurité ont été mises avec succès !", "You security questions have been successfully updated!": "Vos questions de sécurité ont été mises avec succès !",
"You will need to restart the application to apply changes": "Vous aurez besoin de redémarrer l'application pour appliquer les changements", "You will need to restart the application to apply changes": "Vous aurez besoin de redémarrer l'application pour appliquer les changements",

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

@ -14,7 +14,7 @@ import 'package:comunic/utils/conversations_utils.dart';
import 'package:comunic/utils/intl_utils.dart'; import 'package:comunic/utils/intl_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:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher_string.dart';
/// Show information about a Forez member /// Show information about a Forez member
/// ///
@ -165,7 +165,7 @@ class _ForezMemberProfileRouteState extends State<ForezMemberProfileRoute> {
subtitle: Text(tr("Website")!), subtitle: Text(tr("Website")!),
trailing: IconButton( trailing: IconButton(
icon: Icon(Icons.open_in_new), icon: Icon(Icons.open_in_new),
onPressed: () => launch(_user.personalWebsite), onPressed: () => launchUrlString(_user.personalWebsite),
), ),
), ),

View File

@ -4,11 +4,13 @@
// ignore_for_file: directives_ordering // ignore_for_file: directives_ordering
// ignore_for_file: lines_longer_than_80_chars // ignore_for_file: lines_longer_than_80_chars
// ignore_for_file: depend_on_referenced_packages
import 'package:connectivity_plus_web/connectivity_plus_web.dart'; import 'package:connectivity_plus_web/connectivity_plus_web.dart';
import 'package:file_picker/_internal/file_picker_web.dart'; import 'package:file_picker/_internal/file_picker_web.dart';
import 'package:firebase_core_web/firebase_core_web.dart'; import 'package:firebase_core_web/firebase_core_web.dart';
import 'package:firebase_messaging_web/firebase_messaging_web.dart'; import 'package:firebase_messaging_web/firebase_messaging_web.dart';
import 'package:image_cropper_for_web/image_cropper_for_web.dart';
import 'package:image_picker_for_web/image_picker_for_web.dart'; import 'package:image_picker_for_web/image_picker_for_web.dart';
import 'package:package_info_plus_web/package_info_plus_web.dart'; import 'package:package_info_plus_web/package_info_plus_web.dart';
import 'package:shared_preferences_web/shared_preferences_web.dart'; import 'package:shared_preferences_web/shared_preferences_web.dart';
@ -24,6 +26,7 @@ void registerPlugins(Registrar registrar) {
FilePickerWeb.registerWith(registrar); FilePickerWeb.registerWith(registrar);
FirebaseCoreWeb.registerWith(registrar); FirebaseCoreWeb.registerWith(registrar);
FirebaseMessagingWeb.registerWith(registrar); FirebaseMessagingWeb.registerWith(registrar);
ImageCropperPlugin.registerWith(registrar);
ImagePickerPlugin.registerWith(registrar); ImagePickerPlugin.registerWith(registrar);
PackageInfoPlugin.registerWith(registrar); PackageInfoPlugin.registerWith(registrar);
SharedPreferencesPlugin.registerWith(registrar); SharedPreferencesPlugin.registerWith(registrar);

View File

@ -0,0 +1,38 @@
import 'package:comunic/enums/report_target_type.dart';
import 'package:comunic/models/api_request.dart';
import 'package:comunic/models/report_target.dart';
import 'package:comunic/models/server_config.dart';
/// Reports Helper
///
/// @author Pierre Hubert
enum ReportResult {
Success,
ErrorAlreadyReported,
Error,
}
class ReportHelper {
/// Send a new report to the server
static Future<ReportResult> sendReport({
required ReportCause cause,
required ReportTarget target,
required String comment,
}) async {
final response = await APIRequest.withLogin("reports/create", args: {
"cause": cause.id,
"target_type": target.type.apiId,
"target_id": target.id.toString(),
"comment": comment
}).exec();
if (response.isOK) return ReportResult.Success;
print("Failed to send report: ${response.content}");
if (response.code == 409) return ReportResult.ErrorAlreadyReported;
return ReportResult.Error;
}
}

View File

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

@ -6,6 +6,7 @@ import 'package:comunic/lists/comments_list.dart';
import 'package:comunic/models/displayed_content.dart'; import 'package:comunic/models/displayed_content.dart';
import 'package:comunic/models/like_element.dart'; import 'package:comunic/models/like_element.dart';
import 'package:comunic/models/survey.dart'; import 'package:comunic/models/survey.dart';
import 'package:comunic/utils/account_utils.dart' as account;
/// Single post information /// Single post information
/// ///
@ -76,6 +77,8 @@ class Post implements LikeElement {
access == UserAccessLevels.FULL || access == UserAccessLevels.FULL ||
access == UserAccessLevels.INTERMEDIATE; access == UserAccessLevels.INTERMEDIATE;
bool get isOwner => userID == account.userID();
@override @override
LikesType get likeType => LikesType.POST; LikesType get likeType => LikesType.POST;
} }

View File

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

View File

@ -1,4 +1,5 @@
import 'package:comunic/utils/date_utils.dart'; import 'package:comunic/utils/date_utils.dart';
import 'package:comunic/utils/intl_utils.dart';
import 'package:version/version.dart'; import 'package:version/version.dart';
/// Server static configuration /// Server static configuration
@ -137,6 +138,28 @@ class Banner {
bool get visible => enabled && (expire == null || expire! > time()); 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;
final bool canUserReportHisOwnContent;
const ReportPolicy({
required this.causes,
required this.maxCommentLength,
required this.canUserReportHisOwnContent,
});
}
class ServerConfig { class ServerConfig {
final Version minSupportedMobileVersion; final Version minSupportedMobileVersion;
final String termsURL; final String termsURL;
@ -150,6 +173,7 @@ class ServerConfig {
final ServerDataConservationPolicy dataConservationPolicy; final ServerDataConservationPolicy dataConservationPolicy;
final ConversationsPolicy conversationsPolicy; final ConversationsPolicy conversationsPolicy;
final AccountInformationPolicy accountInformationPolicy; final AccountInformationPolicy accountInformationPolicy;
final ReportPolicy? reportPolicy;
const ServerConfig({ const ServerConfig({
required this.minSupportedMobileVersion, required this.minSupportedMobileVersion,
@ -164,5 +188,8 @@ class ServerConfig {
required this.dataConservationPolicy, required this.dataConservationPolicy,
required this.conversationsPolicy, required this.conversationsPolicy,
required this.accountInformationPolicy, required this.accountInformationPolicy,
required this.reportPolicy,
}); });
bool get isReportingEnabled => this.reportPolicy != null;
} }

View File

@ -1,7 +1,7 @@
import 'package:comunic/helpers/server_config_helper.dart'; import 'package:comunic/helpers/server_config_helper.dart';
import 'package:comunic/utils/intl_utils.dart'; import 'package:comunic/utils/intl_utils.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher_string.dart';
/// Deprecation dialog /// Deprecation dialog
/// ///
@ -22,12 +22,12 @@ class _DeprecationDialog extends StatelessWidget {
actions: [ actions: [
MaterialButton( MaterialButton(
onPressed: () => onPressed: () =>
launch(ServerConfigurationHelper.config!.playStoreURL), launchUrlString(ServerConfigurationHelper.config!.playStoreURL),
child: Text(tr("Go to the Play Store")!), child: Text(tr("Go to the Play Store")!),
), ),
MaterialButton( MaterialButton(
onPressed: () => onPressed: () => launchUrlString(
launch(ServerConfigurationHelper.config!.androidDirectDownloadURL), ServerConfigurationHelper.config!.androidDirectDownloadURL),
child: Text(tr("Download update outside Play Store")!), child: Text(tr("Download update outside Play Store")!),
), ),
MaterialButton( MaterialButton(

View File

@ -0,0 +1,140 @@
import 'package:comunic/helpers/report_helper.dart';
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/dialogs/alert_dialog.dart';
import 'package:comunic/ui/routes/main_route/main_route.dart';
import 'package:comunic/ui/widgets/comunic_back_button_widget.dart';
import 'package:comunic/ui/widgets/safe_state.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 {
MainController.of(ctx)!.push(
_ReportDialog(target: target),
canShowAsDialog: true,
hideNavBar: true,
);
}
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 SafeState<_ReportDialog> {
var _cause = srvConfig!.reportPolicy!.causes.length - 1;
var _status = _Status.ChooseCause;
final _commentController = TextEditingController();
List<ReportCause> get _causes => srvConfig!.reportPolicy!.causes;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: ComunicBackButton(),
title: Text(tr("Report abuse")!),
actions: _status == _Status.Sending
? []
: [
IconButton(
onPressed: _goNextStep,
icon: Icon(Icons.check),
),
]),
body: Padding(
padding: const EdgeInsets.all(8.0),
child: _buildContent(),
),
);
}
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:")!),
SizedBox(height: 15),
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,
),
),
],
);
if (_status == _Status.GiveComment) {
return Column(
children: [
Text("You can optionally describe the reason of your report:"),
SizedBox(height: 15),
TextField(
controller: _commentController,
decoration: InputDecoration(hintText: tr("Reason of report")),
minLines: 5,
maxLines: 5,
maxLength: srvConfig!.reportPolicy!.maxCommentLength,
)
],
);
}
throw Exception("Unknown status!");
}
void _goNextStep() async {
if (_status == _Status.ChooseCause) {
setState(() => _status = _Status.GiveComment);
return;
}
// Send report
try {
setState(() => _status = _Status.Sending);
final result = await ReportHelper.sendReport(
cause: _causes[_cause],
target: widget.target,
comment: _commentController.value.text,
);
// In case of success
if (result == ReportResult.Success) {
await alert(context,
tr("Report successfully saved. Thank you for your contribution!"));
MainController.of(context)!.popPage();
} else if (result == ReportResult.ErrorAlreadyReported) {
await alert(
context, tr("You have already sent a report for this resource!"));
MainController.of(context)!.popPage();
} else {
await alert(context, tr("Failed to send report!"));
}
} catch (e, s) {
print("$e $s");
alert(context, tr("Failed to send report!"));
} finally {
setState(() => _status = _Status.GiveComment);
}
}
}

View File

@ -2,7 +2,7 @@ import 'package:cached_network_image/cached_network_image.dart';
import 'package:comunic/utils/intl_utils.dart'; import 'package:comunic/utils/intl_utils.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:photo_view/photo_view.dart'; import 'package:photo_view/photo_view.dart';
import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher_string.dart';
/// Full screen image details /// Full screen image details
/// ///
@ -26,7 +26,7 @@ class _FullScreenImageRouteState extends State<FullScreenImageRoute> {
title: Text(tr("Image")!), title: Text(tr("Image")!),
actions: [ actions: [
IconButton( IconButton(
icon: Icon(Icons.launch), onPressed: () => launch(widget.url)) icon: Icon(Icons.launch), onPressed: () => launchUrlString(widget.url))
], ],
), ),
body: PhotoView( body: PhotoView(

View File

@ -19,7 +19,6 @@ import '../../utils/files_utils.dart';
/// Return original image in case of error / if the user did not crop the image /// Return original image in case of error / if the user did not crop the image
Future<BytesFile> showImageCropper(BuildContext context, BytesFile source, Future<BytesFile> showImageCropper(BuildContext context, BytesFile source,
{CropAspectRatio? aspectRatio}) async { {CropAspectRatio? aspectRatio}) async {
File? file; File? file;
File? cropped; File? cropped;
@ -27,15 +26,18 @@ Future<BytesFile> showImageCropper(BuildContext context, BytesFile source,
file = await generateTemporaryFile(); file = await generateTemporaryFile();
await file.writeAsBytes(source.bytes!); await file.writeAsBytes(source.bytes!);
File? cropped = await ImageCropper().cropImage( var cropped = await ImageCropper().cropImage(
sourcePath: file.absolute.path, sourcePath: file.absolute.path,
compressFormat: ImageCompressFormat.png, compressFormat: ImageCompressFormat.png,
aspectRatio: aspectRatio, aspectRatio: aspectRatio,
androidUiSettings: AndroidUiSettings( uiSettings: [
toolbarColor: Colors.black, AndroidUiSettings(
toolbarTitle: tr("Crop Photo"), toolbarColor: Colors.black,
toolbarWidgetColor: Colors.white, toolbarTitle: tr("Crop Photo"),
), toolbarWidgetColor: Colors.white,
),
IOSUiSettings(title: tr("Crop Photo"))
],
); );
if (cropped == null) return source; if (cropped == null) return source;

View File

@ -5,7 +5,7 @@ import 'package:comunic/utils/intl_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:flutter_settings_ui/flutter_settings_ui.dart'; import 'package:flutter_settings_ui/flutter_settings_ui.dart';
import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher_string.dart';
/// About application settings /// About application settings
/// ///
@ -24,11 +24,11 @@ class AboutApplicationSettings extends StatelessWidget {
tiles: [ tiles: [
SettingsTile( SettingsTile(
title: tr("Privacy policy"), title: tr("Privacy policy"),
onPressed: (c) => launch(srvConfig!.privacyPolicyURL), onPressed: (c) => launchUrlString(srvConfig!.privacyPolicyURL),
), ),
SettingsTile( SettingsTile(
title: tr("Terms of Use"), title: tr("Terms of Use"),
onPressed: (c) => launch(srvConfig!.termsURL), onPressed: (c) => launchUrlString(srvConfig!.termsURL),
), ),
SettingsTile( SettingsTile(
title: tr("Contact us"), title: tr("Contact us"),

View File

@ -1,4 +1,8 @@
import 'package:comunic/enums/report_target_type.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/report_target.dart';
import 'package:comunic/ui/dialogs/report_dialog.dart';
import 'package:comunic/ui/routes/main_route/main_route.dart'; import 'package:comunic/ui/routes/main_route/main_route.dart';
import 'package:comunic/ui/screens/group_sections/about_group_section.dart'; import 'package:comunic/ui/screens/group_sections/about_group_section.dart';
import 'package:comunic/ui/screens/group_sections/forez_presence_section.dart'; import 'package:comunic/ui/screens/group_sections/forez_presence_section.dart';
@ -65,7 +69,10 @@ class _AuthorizedGroupPageScreenState
// About the group // About the group
_GroupPageTab( _GroupPageTab(
widget: (c) => AboutGroupSection(group: _group), widget: (c) => AboutGroupSection(
group: _group,
onReportGroup: _reportGroup,
),
label: tr("About")!, label: tr("About")!,
), ),
@ -197,6 +204,10 @@ class _AuthorizedGroupPageScreenState
), ),
); );
} }
/// Report group
void _reportGroup(Group g) => showReportDialog(
ctx: context, target: ReportTarget(ReportTargetType.Group, g.id));
} }
class _GroupPageTab { class _GroupPageTab {

View File

@ -1,5 +1,6 @@
import 'dart:async'; import 'dart:async';
import 'package:comunic/enums/report_target_type.dart';
import 'package:comunic/helpers/conversations_helper.dart'; import 'package:comunic/helpers/conversations_helper.dart';
import 'package:comunic/helpers/events_helper.dart'; import 'package:comunic/helpers/events_helper.dart';
import 'package:comunic/helpers/server_config_helper.dart'; import 'package:comunic/helpers/server_config_helper.dart';
@ -11,7 +12,9 @@ import 'package:comunic/models/config.dart';
import 'package:comunic/models/conversation.dart'; import 'package:comunic/models/conversation.dart';
import 'package:comunic/models/conversation_message.dart'; import 'package:comunic/models/conversation_message.dart';
import 'package:comunic/models/new_conversation_message.dart'; import 'package:comunic/models/new_conversation_message.dart';
import 'package:comunic/models/report_target.dart';
import 'package:comunic/ui/dialogs/pick_file_dialog.dart'; import 'package:comunic/ui/dialogs/pick_file_dialog.dart';
import 'package:comunic/ui/dialogs/report_dialog.dart';
import 'package:comunic/ui/routes/main_route/main_route.dart'; import 'package:comunic/ui/routes/main_route/main_route.dart';
import 'package:comunic/ui/tiles/conversation_message_tile.dart'; import 'package:comunic/ui/tiles/conversation_message_tile.dart';
import 'package:comunic/ui/tiles/server_conversation_message_tile.dart'; import 'package:comunic/ui/tiles/server_conversation_message_tile.dart';
@ -504,6 +507,7 @@ class _ConversationScreenState extends SafeState<ConversationScreen> {
onRequestMessageStats: _requestMessageStats, onRequestMessageStats: _requestMessageStats,
onRequestMessageUpdate: _updateMessage, onRequestMessageUpdate: _updateMessage,
onRequestMessageDelete: _deleteMessage, onRequestMessageDelete: _deleteMessage,
onReportMessage: _reportMessage,
); );
Widget _buildDateWidget(DateTime dt) => Center( Widget _buildDateWidget(DateTime dt) => Center(
@ -753,4 +757,9 @@ class _ConversationScreenState extends SafeState<ConversationScreen> {
if (!await _conversationsHelper.deleteMessage(message.id)) if (!await _conversationsHelper.deleteMessage(message.id))
showSimpleSnack(context, tr("Could not delete conversation message!")!); showSimpleSnack(context, tr("Could not delete conversation message!")!);
} }
/// Report message
void _reportMessage(ConversationMessage msg) => showReportDialog(
ctx: context,
target: ReportTarget(ReportTargetType.ConversationMessage, msg.id!));
} }

View File

@ -1,6 +1,7 @@
import 'dart:math'; import 'dart:math';
import 'package:comunic/enums/load_error_level.dart'; import 'package:comunic/enums/load_error_level.dart';
import 'package:comunic/enums/report_target_type.dart';
import 'package:comunic/helpers/conversations_helper.dart'; import 'package:comunic/helpers/conversations_helper.dart';
import 'package:comunic/helpers/events_helper.dart'; import 'package:comunic/helpers/events_helper.dart';
import 'package:comunic/helpers/groups_helper.dart'; import 'package:comunic/helpers/groups_helper.dart';
@ -9,6 +10,8 @@ import 'package:comunic/lists/conversations_list.dart';
import 'package:comunic/lists/groups_list.dart'; import 'package:comunic/lists/groups_list.dart';
import 'package:comunic/lists/users_list.dart'; import 'package:comunic/lists/users_list.dart';
import 'package:comunic/models/conversation.dart'; import 'package:comunic/models/conversation.dart';
import 'package:comunic/models/report_target.dart';
import 'package:comunic/ui/dialogs/report_dialog.dart';
import 'package:comunic/ui/routes/main_route/main_route.dart'; import 'package:comunic/ui/routes/main_route/main_route.dart';
import 'package:comunic/ui/screens/create_conversation_screen.dart'; import 'package:comunic/ui/screens/create_conversation_screen.dart';
import 'package:comunic/ui/tiles/conversation_tile.dart'; import 'package:comunic/ui/tiles/conversation_tile.dart';
@ -160,6 +163,11 @@ class _ConversationScreenState extends SafeState<ConversationsListScreen> {
_loadConversationsList(false); _loadConversationsList(false);
} }
/// Handle conversation report request
void _reportConversation(Conversation conversation) => showReportDialog(
ctx: context,
target: ReportTarget(ReportTargetType.Conversation, conversation.id!));
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
if (_error == LoadErrorLevel.MAJOR) return _buildErrorCard(); if (_error == LoadErrorLevel.MAJOR) return _buildErrorCard();
@ -201,6 +209,7 @@ class _ConversationScreenState extends SafeState<ConversationsListScreen> {
}, },
onRequestUpdate: _updateConversation, onRequestUpdate: _updateConversation,
onRequestLeave: _requestLeaveConversation, onRequestLeave: _requestLeaveConversation,
onReport: _reportConversation,
); );
}, },
itemCount: max(_list!.length, 1), itemCount: max(_list!.length, 1),

View File

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

View File

@ -1,9 +1,10 @@
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/utils/date_utils.dart'; import 'package:comunic/utils/date_utils.dart';
import 'package:comunic/utils/intl_utils.dart'; import 'package:comunic/utils/intl_utils.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher_string.dart';
/// About group section /// About group section
/// ///
@ -11,10 +12,12 @@ import 'package:url_launcher/url_launcher.dart';
class AboutGroupSection extends StatelessWidget { class AboutGroupSection extends StatelessWidget {
final AdvancedGroupInfo group; final AdvancedGroupInfo group;
final Function(Group) onReportGroup;
const AboutGroupSection({ const AboutGroupSection({
Key? key, Key? key,
required this.group, required this.group,
required this.onReportGroup,
}) : super(key: key); }) : super(key: key);
@override @override
@ -26,7 +29,7 @@ class AboutGroupSection extends StatelessWidget {
leading: Icon(Icons.link), leading: Icon(Icons.link),
title: Text(tr("URL")!), title: Text(tr("URL")!),
subtitle: Text(group.url), subtitle: Text(group.url),
onTap: () => launch(group.url), onTap: () => launchUrlString(group.url),
) )
: Container(), : Container(),
@ -103,6 +106,15 @@ class AboutGroupSection extends StatelessWidget {
subtitle: Text(tr("Forez special features enabled")!), subtitle: Text(tr("Forez special features enabled")!),
) )
: Container(), : Container(),
// Report group
srvConfig!.isReportingEnabled
? ListTile(
textColor: Colors.red,
leading: Icon(Icons.flag, color: Colors.red),
title: Text(tr("Report abuse")!),
onTap: () => onReportGroup(group),
)
: Container(),
], ],
); );
} }

View File

@ -1,6 +1,10 @@
import 'package:comunic/enums/report_target_type.dart';
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/lists/groups_list.dart'; import 'package:comunic/lists/groups_list.dart';
import 'package:comunic/models/group.dart'; import 'package:comunic/models/group.dart';
import 'package:comunic/models/report_target.dart';
import 'package:comunic/ui/dialogs/report_dialog.dart';
import 'package:comunic/ui/routes/main_route/main_route.dart'; import 'package:comunic/ui/routes/main_route/main_route.dart';
import 'package:comunic/ui/widgets/group_icon_widget.dart'; import 'package:comunic/ui/widgets/group_icon_widget.dart';
import 'package:comunic/ui/widgets/group_membership_widget.dart'; import 'package:comunic/ui/widgets/group_membership_widget.dart';
@ -70,9 +74,26 @@ class _GroupsListScreenState extends SafeState<GroupsListScreen> {
group: g, group: g,
onUpdated: () => _refreshIndicatorKey.currentState!.show(), onUpdated: () => _refreshIndicatorKey.currentState!.show(),
), ),
trailing: IconButton( trailing: IntrinsicWidth(
icon: Icon(Icons.delete), child: Row(
onPressed: () => _deleteGroup(g)), children: [
// Remove membership
IconButton(
icon: Icon(Icons.delete),
onPressed: () => _deleteGroup(g)),
// Report button
srvConfig!.isReportingEnabled && !g.isAtLeastMember
? IconButton(
onPressed: () => _reportGroup(g),
icon: Icon(
Icons.flag,
),
)
: Container()
],
),
),
onTap: () => MainController.of(context)!.openGroup(g.id), onTap: () => MainController.of(context)!.openGroup(g.id),
)) ))
.toList(), .toList(),
@ -122,6 +143,10 @@ class _GroupsListScreenState extends SafeState<GroupsListScreen> {
_refreshIndicatorKey.currentState!.show(); _refreshIndicatorKey.currentState!.show();
} }
/// Report a group
void _reportGroup(Group g) => showReportDialog(
ctx: context, target: ReportTarget(ReportTargetType.Group, g.id));
/// Add a group /// Add a group
void _createGroup() async { void _createGroup() async {
try { try {

View File

@ -20,8 +20,7 @@ enum _PageStatus { LOADING, ERROR, READY }
class UserPageScreen extends StatefulWidget { class UserPageScreen extends StatefulWidget {
final int userID; final int userID;
const UserPageScreen({Key? key, required this.userID}) const UserPageScreen({Key? key, required this.userID}) : super(key: key);
: super(key: key);
@override @override
_UserPageScreenState createState() => _UserPageScreenState(); _UserPageScreenState createState() => _UserPageScreenState();
@ -33,7 +32,7 @@ class _UserPageScreenState extends SafeState<UserPageScreen> {
// Objects members // Objects members
_PageStatus _status = _PageStatus.LOADING; _PageStatus _status = _PageStatus.LOADING;
AdvancedUserInfo? _userInfo; AdvancedUserInfo? _userInfo;
FriendStatus? _frienshipStatus; FriendStatus? _frienshipStatus;
final _refreshIndicatorKey = GlobalKey<RefreshIndicatorState>(); final _refreshIndicatorKey = GlobalKey<RefreshIndicatorState>();
@ -42,7 +41,7 @@ class _UserPageScreenState extends SafeState<UserPageScreen> {
@override @override
void didChangeDependencies() { void didChangeDependencies() {
super.didChangeDependencies(); super.didChangeDependencies();
if(_userInfo?.id != widget.userID) if (_userInfo?.id == widget.userID) return;
_getUserInfo(); _getUserInfo();
} }

View File

@ -1,8 +1,12 @@
import 'package:comunic/enums/report_target_type.dart';
import 'package:comunic/enums/user_page_visibility.dart'; import 'package:comunic/enums/user_page_visibility.dart';
import 'package:comunic/helpers/friends_helper.dart'; import 'package:comunic/helpers/friends_helper.dart';
import 'package:comunic/helpers/server_config_helper.dart';
import 'package:comunic/models/advanced_user_info.dart'; import 'package:comunic/models/advanced_user_info.dart';
import 'package:comunic/models/displayed_content.dart'; import 'package:comunic/models/displayed_content.dart';
import 'package:comunic/models/friend_status.dart'; import 'package:comunic/models/friend_status.dart';
import 'package:comunic/models/report_target.dart';
import 'package:comunic/ui/dialogs/report_dialog.dart';
import 'package:comunic/ui/widgets/FrienshipStatusWidget.dart'; import 'package:comunic/ui/widgets/FrienshipStatusWidget.dart';
import 'package:comunic/ui/widgets/async_screen_widget.dart'; import 'package:comunic/ui/widgets/async_screen_widget.dart';
import 'package:comunic/ui/widgets/text_widget.dart'; import 'package:comunic/ui/widgets/text_widget.dart';
@ -13,7 +17,7 @@ import 'package:comunic/utils/intl_utils.dart';
/// ///
/// @author Pierre Hubert /// @author Pierre Hubert
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher_string.dart';
class AboutUserSection extends StatefulWidget { class AboutUserSection extends StatefulWidget {
final AdvancedUserInfo user; final AdvancedUserInfo user;
@ -21,7 +25,7 @@ class AboutUserSection extends StatefulWidget {
const AboutUserSection({ const AboutUserSection({
Key? key, Key? key,
required this.user, required this.user,
}) : super(key: key); }) : super(key: key);
@override @override
_AboutUserSectionState createState() => _AboutUserSectionState(); _AboutUserSectionState createState() => _AboutUserSectionState();
@ -59,7 +63,7 @@ class _AboutUserSectionState extends State<AboutUserSection> {
leading: Icon(Icons.link), leading: Icon(Icons.link),
title: Text(tr("Personal Website")!), title: Text(tr("Personal Website")!),
subtitle: Text(widget.user.personalWebsite), subtitle: Text(widget.user.personalWebsite),
onTap: () => launch(widget.user.personalWebsite), onTap: () => launchUrlString(widget.user.personalWebsite),
) )
: Container(), : Container(),
@ -129,6 +133,23 @@ class _AboutUserSectionState extends State<AboutUserSection> {
? tr("Public page")! ? tr("Public page")!
: tr("Private page")!)), : tr("Private page")!)),
), ),
// Report user
srvConfig!.isReportingEnabled &&
(!widget.user.isCurrentUser ||
srvConfig!.reportPolicy!.canUserReportHisOwnContent)
? ListTile(
textColor: Colors.red,
leading: Icon(Icons.flag, color: Colors.red),
title: Text(tr("Report abuse")!),
onTap: _reportAbuse,
)
: Container(),
], ],
); );
/// Report user
void _reportAbuse() => showReportDialog(
ctx: context,
target: ReportTarget(ReportTargetType.User, widget.user.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/friend.dart';
import 'package:comunic/models/user.dart'; import 'package:comunic/models/user.dart';
import 'package:comunic/ui/widgets/account_image_widget.dart'; import 'package:comunic/ui/widgets/account_image_widget.dart';
@ -10,11 +11,17 @@ import 'package:flutter/material.dart';
/// ///
/// @author Pierre HUBERT /// @author Pierre HUBERT
enum _FriendMenuChoices { REMOVE, TOGGLE_FOLLOWING, PRIVATE_CONVERSATION } enum _FriendMenuChoices {
REMOVE,
TOGGLE_FOLLOWING,
PRIVATE_CONVERSATION,
REPORT
}
typedef OnRequestDeleteFriend = void Function(Friend); typedef OnRequestDeleteFriend = void Function(Friend);
typedef OnSetFollowing = void Function(Friend, bool); typedef OnSetFollowing = void Function(Friend, bool);
typedef OnOpenPrivateConversation = void Function(Friend); typedef OnOpenPrivateConversation = void Function(Friend);
typedef OnReportFriend = void Function(Friend);
class AcceptedFriendTile extends StatelessWidget { class AcceptedFriendTile extends StatelessWidget {
final Friend friend; final Friend friend;
@ -22,6 +29,7 @@ class AcceptedFriendTile extends StatelessWidget {
final OnRequestDeleteFriend onRequestDelete; final OnRequestDeleteFriend onRequestDelete;
final OnSetFollowing onSetFollowing; final OnSetFollowing onSetFollowing;
final OnOpenPrivateConversation onOpenPrivateConversation; final OnOpenPrivateConversation onOpenPrivateConversation;
final OnReportFriend onReportFriend;
const AcceptedFriendTile({ const AcceptedFriendTile({
Key? key, Key? key,
@ -30,6 +38,7 @@ class AcceptedFriendTile extends StatelessWidget {
required this.onRequestDelete, required this.onRequestDelete,
required this.onSetFollowing, required this.onSetFollowing,
required this.onOpenPrivateConversation, required this.onOpenPrivateConversation,
required this.onReportFriend,
}) : super(key: key); }) : super(key: key);
@override @override
@ -75,7 +84,15 @@ class AcceptedFriendTile extends StatelessWidget {
child: Text(tr("Remove")!), child: Text(tr("Remove")!),
value: _FriendMenuChoices.REMOVE, value: _FriendMenuChoices.REMOVE,
), ),
], ]..addAll(srvConfig!.isReportingEnabled
? [
// Report user
PopupMenuItem(
child: Text(tr("Report abuse")!),
value: _FriendMenuChoices.REPORT,
),
]
: []),
onSelected: _selectedMenuOption, onSelected: _selectedMenuOption,
), ),
); );
@ -97,6 +114,10 @@ class AcceptedFriendTile extends StatelessWidget {
case _FriendMenuChoices.REMOVE: case _FriendMenuChoices.REMOVE:
onRequestDelete(friend); onRequestDelete(friend);
break; break;
case _FriendMenuChoices.REPORT:
onReportFriend(friend);
break;
} }
} }
} }

View File

@ -1,3 +1,4 @@
import 'package:comunic/helpers/server_config_helper.dart';
import 'package:comunic/models/comment.dart'; import 'package:comunic/models/comment.dart';
import 'package:comunic/models/user.dart'; import 'package:comunic/models/user.dart';
import 'package:comunic/ui/widgets/account_image_widget.dart'; import 'package:comunic/ui/widgets/account_image_widget.dart';
@ -13,13 +14,14 @@ import 'package:flutter/material.dart';
/// ///
/// @author Pierre HUBERT /// @author Pierre HUBERT
enum _CommentAction { DELETE, UPDATE } enum _CommentAction { DELETE, UPDATE, REPORT }
class CommentTile extends StatelessWidget { class CommentTile extends StatelessWidget {
final Comment comment; final Comment comment;
final User user; final User user;
final void Function(Comment) onUpdateComment; final void Function(Comment) onUpdateComment;
final void Function(Comment) onDeleteComment; final void Function(Comment) onDeleteComment;
final void Function(Comment) onReportComment;
const CommentTile({ const CommentTile({
Key? key, Key? key,
@ -27,6 +29,7 @@ class CommentTile extends StatelessWidget {
required this.user, required this.user,
required this.onUpdateComment, required this.onUpdateComment,
required this.onDeleteComment, required this.onDeleteComment,
required this.onReportComment,
}) : super(key: key); }) : super(key: key);
@override @override
@ -37,34 +40,13 @@ class CommentTile extends StatelessWidget {
user.displayName, user.displayName,
), ),
subtitle: _buildCommentContent(), subtitle: _buildCommentContent(),
trailing: Text( trailing: _buildTrailing(),
diffTimeFromNowToStr(comment.timeSent)!,
style: TextStyle(fontSize: 10.0),
),
); );
} }
Widget _buildAccountImageWidget() { Widget _buildAccountImageWidget() {
return PopupMenuButton<_CommentAction>( return AccountImageWidget(
child: AccountImageWidget( user: user,
user: user,
),
onSelected: _selectedMenuOption,
itemBuilder: (c) => [
// Update comment content
PopupMenuItem(
enabled: comment.isOwner,
child: Text(tr("Update")!),
value: _CommentAction.UPDATE,
),
// Delete comment
PopupMenuItem(
enabled: comment.isOwner,
child: Text(tr("Delete")!),
value: _CommentAction.DELETE,
),
],
); );
} }
@ -101,6 +83,51 @@ class CommentTile extends StatelessWidget {
); );
} }
Widget _buildTrailing() {
return IntrinsicWidth(
child: Row(
children: [
Text(
diffTimeFromNowToStr(comment.timeSent)!,
style: TextStyle(fontSize: 10.0),
),
SizedBox(width: 5),
PopupMenuButton<_CommentAction>(
padding: EdgeInsets.all(0),
child: InkWell(
child: Icon(Icons.adaptive.more, size: 20),
),
onSelected: _selectedMenuOption,
itemBuilder: (c) => [
// Update comment content
PopupMenuItem(
enabled: comment.isOwner,
child: Text(tr("Update")!),
value: _CommentAction.UPDATE,
),
// Delete comment
PopupMenuItem(
enabled: comment.isOwner,
child: Text(tr("Delete")!),
value: _CommentAction.DELETE,
),
]..addAll(srvConfig!.isReportingEnabled &&
(!comment.isOwner ||
srvConfig!.reportPolicy!.canUserReportHisOwnContent)
? [
PopupMenuItem(
child: Text(tr("Report abuse")!),
value: _CommentAction.REPORT,
)
]
: []),
)
],
),
);
}
/// A menu option has been selected /// A menu option has been selected
void _selectedMenuOption(_CommentAction value) { void _selectedMenuOption(_CommentAction value) {
switch (value) { switch (value) {
@ -113,6 +140,11 @@ class CommentTile extends StatelessWidget {
case _CommentAction.DELETE: case _CommentAction.DELETE:
onDeleteComment(comment); onDeleteComment(comment);
break; break;
// Report comment
case _CommentAction.REPORT:
onReportComment(comment);
break;
} }
} }
} }

View File

@ -1,3 +1,4 @@
import 'package:comunic/helpers/server_config_helper.dart';
import 'package:comunic/models/conversation_message.dart'; import 'package:comunic/models/conversation_message.dart';
import 'package:comunic/models/user.dart'; import 'package:comunic/models/user.dart';
import 'package:comunic/ui/widgets/conversation_file_tile.dart'; import 'package:comunic/ui/widgets/conversation_file_tile.dart';
@ -17,6 +18,7 @@ enum _MenuChoices {
DELETE, DELETE,
REQUEST_UPDATE_CONTENT, REQUEST_UPDATE_CONTENT,
GET_STATS, GET_STATS,
REPORT
} }
typedef OnRequestMessageStats = void Function(ConversationMessage); typedef OnRequestMessageStats = void Function(ConversationMessage);
@ -29,6 +31,7 @@ class ConversationMessageTile extends StatelessWidget {
final OnRequestMessageStats onRequestMessageStats; final OnRequestMessageStats onRequestMessageStats;
final OnRequestMessageUpdate onRequestMessageUpdate; final OnRequestMessageUpdate onRequestMessageUpdate;
final OnRequestMessageDelete onRequestMessageDelete; final OnRequestMessageDelete onRequestMessageDelete;
final Function(ConversationMessage) onReportMessage;
const ConversationMessageTile({ const ConversationMessageTile({
Key? key, Key? key,
@ -37,6 +40,7 @@ class ConversationMessageTile extends StatelessWidget {
required this.onRequestMessageStats, required this.onRequestMessageStats,
required this.onRequestMessageUpdate, required this.onRequestMessageUpdate,
required this.onRequestMessageDelete, required this.onRequestMessageDelete,
required this.onReportMessage,
}) : super(key: key); }) : super(key: key);
@override @override
@ -90,6 +94,16 @@ class ConversationMessageTile extends StatelessWidget {
value: _MenuChoices.DELETE, value: _MenuChoices.DELETE,
child: Text(tr("Delete")!), child: Text(tr("Delete")!),
), ),
// Report the message
PopupMenuItem(
enabled: srvConfig!.isReportingEnabled &&
(!message.isOwner ||
srvConfig!.reportPolicy!
.canUserReportHisOwnContent),
value: _MenuChoices.REPORT,
child: Text(tr("Report abuse")!),
),
]..removeWhere((element) => !element.enabled), ]..removeWhere((element) => !element.enabled),
), ),
) )
@ -133,6 +147,10 @@ class ConversationMessageTile extends StatelessWidget {
case _MenuChoices.DELETE: case _MenuChoices.DELETE:
onRequestMessageDelete(message); onRequestMessageDelete(message);
break; break;
case _MenuChoices.REPORT:
onReportMessage(message);
break;
} }
} }
} }

View File

@ -1,4 +1,5 @@
import 'package:comunic/helpers/conversations_helper.dart'; import 'package:comunic/helpers/conversations_helper.dart';
import 'package:comunic/helpers/server_config_helper.dart';
import 'package:comunic/lists/groups_list.dart'; import 'package:comunic/lists/groups_list.dart';
import 'package:comunic/lists/users_list.dart'; import 'package:comunic/lists/users_list.dart';
import 'package:comunic/models/config.dart'; import 'package:comunic/models/config.dart';
@ -18,7 +19,7 @@ typedef OpenConversationCallback = void Function(Conversation);
typedef RequestLeaveConversationCallback = void Function(Conversation); typedef RequestLeaveConversationCallback = void Function(Conversation);
typedef RequestUpdateConversationCallback = void Function(Conversation); typedef RequestUpdateConversationCallback = void Function(Conversation);
enum _PopupMenuChoices { UPDATE, LEAVE } enum _PopupMenuChoices { UPDATE, LEAVE, REPORT }
class ConversationTile extends StatelessWidget { class ConversationTile extends StatelessWidget {
final Conversation conversation; final Conversation conversation;
@ -27,6 +28,7 @@ class ConversationTile extends StatelessWidget {
final OpenConversationCallback onOpen; final OpenConversationCallback onOpen;
final RequestUpdateConversationCallback onRequestUpdate; final RequestUpdateConversationCallback onRequestUpdate;
final RequestLeaveConversationCallback onRequestLeave; final RequestLeaveConversationCallback onRequestLeave;
final Function(Conversation) onReport;
const ConversationTile({ const ConversationTile({
Key? key, Key? key,
@ -36,7 +38,8 @@ class ConversationTile extends StatelessWidget {
required this.onOpen, required this.onOpen,
required this.onRequestUpdate, required this.onRequestUpdate,
required this.onRequestLeave, required this.onRequestLeave,
}) : super(key: key); required this.onReport,
}) : super(key: key);
_buildSubInformation(IconData icon, String content) { _buildSubInformation(IconData icon, String content) {
return Row( return Row(
@ -116,18 +119,26 @@ class ConversationTile extends StatelessWidget {
onLongPressOpenMenu: (position) { onLongPressOpenMenu: (position) {
showMenu<_PopupMenuChoices>( showMenu<_PopupMenuChoices>(
context: context, context: context,
position: position, position: position,
items: [ items: [
PopupMenuItem( PopupMenuItem(
child: Text(tr("Update")!), child: Text(tr("Update")!),
value: _PopupMenuChoices.UPDATE, value: _PopupMenuChoices.UPDATE,
), ),
PopupMenuItem( PopupMenuItem(
child: Text(tr("Leave")!), child: Text(tr("Leave")!),
value: _PopupMenuChoices.LEAVE, value: _PopupMenuChoices.LEAVE,
) )
]).then(_conversationMenuCallback); ]..addAll(srvConfig!.isReportingEnabled
? [
PopupMenuItem(
child: Text(tr("Report abuse")!),
value: _PopupMenuChoices.REPORT,
)
]
: []))
.then(_conversationMenuCallback);
}, },
); );
@ -158,7 +169,11 @@ class ConversationTile extends StatelessWidget {
onRequestLeave(conversation); onRequestLeave(conversation);
break; break;
default: case _PopupMenuChoices.REPORT:
onReport(conversation);
break;
case null:
break; 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/friend.dart';
import 'package:comunic/models/user.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/ui/widgets/account_image_widget.dart';
import 'package:comunic/utils/intl_utils.dart'; import 'package:comunic/utils/intl_utils.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -14,13 +16,15 @@ class PendingFriendTile extends StatelessWidget {
final Friend friend; final Friend friend;
final User user; final User user;
final RespondFriendshipRequestCallback onRespond; final RespondFriendshipRequestCallback onRespond;
final OnReportFriend onReport;
const PendingFriendTile( const PendingFriendTile({
{Key? key, Key? key,
required this.friend, required this.friend,
required this.user, required this.user,
required this.onRespond}) required this.onRespond,
: super(key: key); required this.onReport,
}) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -56,7 +60,19 @@ class PendingFriendTile extends StatelessWidget {
style: ButtonStyle( style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(Colors.red)), backgroundColor: MaterialStateProperty.all(Colors.red)),
onPressed: () => onRespond(friend, false), onPressed: () => onRespond(friend, false),
) ),
// Report button
srvConfig!.isReportingEnabled
? IconButton(
visualDensity: VisualDensity.compact,
onPressed: () => onReport(friend),
icon: Icon(
Icons.flag,
size: 15.0,
),
)
: Container()
], ],
), ),
), ),

View File

@ -2,15 +2,19 @@ import 'dart:math';
import 'package:comunic/enums/post_kind.dart'; import 'package:comunic/enums/post_kind.dart';
import 'package:comunic/enums/post_visibility_level.dart'; import 'package:comunic/enums/post_visibility_level.dart';
import 'package:comunic/enums/report_target_type.dart';
import 'package:comunic/helpers/comments_helper.dart'; import 'package:comunic/helpers/comments_helper.dart';
import 'package:comunic/helpers/posts_helper.dart'; import 'package:comunic/helpers/posts_helper.dart';
import 'package:comunic/helpers/server_config_helper.dart';
import 'package:comunic/lists/groups_list.dart'; import 'package:comunic/lists/groups_list.dart';
import 'package:comunic/lists/users_list.dart'; import 'package:comunic/lists/users_list.dart';
import 'package:comunic/models/comment.dart'; import 'package:comunic/models/comment.dart';
import 'package:comunic/models/new_comment.dart'; import 'package:comunic/models/new_comment.dart';
import 'package:comunic/models/post.dart'; import 'package:comunic/models/post.dart';
import 'package:comunic/models/report_target.dart';
import 'package:comunic/models/user.dart'; import 'package:comunic/models/user.dart';
import 'package:comunic/ui/dialogs/post_visibility_picker_dialog.dart'; import 'package:comunic/ui/dialogs/post_visibility_picker_dialog.dart';
import 'package:comunic/ui/dialogs/report_dialog.dart';
import 'package:comunic/ui/tiles/comment_tile.dart'; import 'package:comunic/ui/tiles/comment_tile.dart';
import 'package:comunic/ui/widgets/account_image_widget.dart'; import 'package:comunic/ui/widgets/account_image_widget.dart';
import 'package:comunic/ui/widgets/countdown_widget.dart'; import 'package:comunic/ui/widgets/countdown_widget.dart';
@ -25,7 +29,7 @@ import 'package:comunic/utils/intl_utils.dart';
import 'package:comunic/utils/navigation_utils.dart'; import 'package:comunic/utils/navigation_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:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher_string.dart';
import '../../models/api_request.dart'; import '../../models/api_request.dart';
import '../../utils/log_utils.dart'; import '../../utils/log_utils.dart';
@ -41,7 +45,7 @@ const TextStyle _userNameStyle = TextStyle(
fontSize: 16.0); fontSize: 16.0);
/// Post actions /// Post actions
enum _PostActions { DELETE, UPDATE_CONTENT } enum _PostActions { DELETE, UPDATE_CONTENT, REPORT }
class PostTile extends StatefulWidget { class PostTile extends StatefulWidget {
final Post post; final Post post;
@ -152,7 +156,16 @@ class _PostTileState extends State<PostTile> {
value: _PostActions.DELETE, value: _PostActions.DELETE,
enabled: widget.post.canDelete, enabled: widget.post.canDelete,
), ),
], ]..addAll(srvConfig!.isReportingEnabled &&
(!widget.post.isOwner ||
srvConfig!.reportPolicy!.canUserReportHisOwnContent)
? [
PopupMenuItem(
child: Text(tr("Report abuse")!),
value: _PostActions.REPORT,
)
]
: []),
onSelected: _selectedPostMenuAction, onSelected: _selectedPostMenuAction,
) )
], ],
@ -270,8 +283,8 @@ class _PostTileState extends State<PostTile> {
Text(tr("YouTube movie")!) Text(tr("YouTube movie")!)
], ],
), ),
onPressed: () => onPressed: () => launchUrlString(
launch("https://youtube.com/watch/?v=" + widget.post.filePath!), "https://youtube.com/watch/?v=" + widget.post.filePath!),
); );
} }
@ -280,7 +293,7 @@ class _PostTileState extends State<PostTile> {
color: color:
darkTheme() ? darkerAccentColor : Color.fromRGBO(0xf7, 0xf7, 0xf7, 1), darkTheme() ? darkerAccentColor : Color.fromRGBO(0xf7, 0xf7, 0xf7, 1),
child: InkWell( child: InkWell(
onTap: () => launch(widget.post.linkURL!), onTap: () => launchUrlString(widget.post.linkURL!),
child: Row( child: Row(
children: <Widget>[ children: <Widget>[
Padding( Padding(
@ -319,7 +332,7 @@ class _PostTileState extends State<PostTile> {
Widget _buildPostPDF() { Widget _buildPostPDF() {
return ElevatedButton.icon( return ElevatedButton.icon(
onPressed: () { onPressed: () {
launch(widget.post.fileURL!); launchUrlString(widget.post.fileURL!);
}, },
icon: Icon(Icons.picture_as_pdf), icon: Icon(Icons.picture_as_pdf),
label: Text(tr("PDF")!), label: Text(tr("PDF")!),
@ -356,6 +369,7 @@ class _PostTileState extends State<PostTile> {
user: widget.usersInfo.getUser(comment.userID), user: widget.usersInfo.getUser(comment.userID),
onUpdateComment: _updateCommentContent, onUpdateComment: _updateCommentContent,
onDeleteComment: _deleteComment, onDeleteComment: _deleteComment,
onReportComment: _reportComment,
); );
}, },
); );
@ -549,6 +563,10 @@ class _PostTileState extends State<PostTile> {
} }
} }
/// Process a report request
void _reportComment(Comment comment) => showReportDialog(
ctx: context, target: ReportTarget(ReportTargetType.Comment, comment.id));
/// Method called each time the user has selected an option /// Method called each time the user has selected an option
void _selectedPostMenuAction(_PostActions value) { void _selectedPostMenuAction(_PostActions value) {
switch (value) { switch (value) {
@ -561,6 +579,11 @@ class _PostTileState extends State<PostTile> {
case _PostActions.DELETE: case _PostActions.DELETE:
confirmDelete(); confirmDelete();
break; break;
// Report content
case _PostActions.REPORT:
reportContent();
break;
} }
} }
@ -620,4 +643,9 @@ class _PostTileState extends State<PostTile> {
widget.onDeletedPost(widget.post); widget.onDeletedPost(widget.post);
} }
/// Report post
void reportContent() async => await showReportDialog(
ctx: context,
target: ReportTarget(ReportTargetType.Post, widget.post.id));
} }

View File

@ -5,7 +5,7 @@ import 'package:comunic/models/server_config.dart';
import 'package:comunic/utils/date_utils.dart'; import 'package:comunic/utils/date_utils.dart';
import 'package:comunic/utils/intl_utils.dart'; import 'package:comunic/utils/intl_utils.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher_string.dart';
bool _bannerDismissed = false; bool _bannerDismissed = false;
@ -26,7 +26,7 @@ class _BannerWidgetState extends State<BannerWidget> {
} }
void _openLink() { void _openLink() {
launch(srvConfig!.banner!.link!); launchUrlString(srvConfig!.banner!.link!);
} }
@override @override

View File

@ -9,7 +9,7 @@ import 'package:comunic/ui/routes/video_player_route.dart';
import 'package:comunic/ui/widgets/network_image_widget.dart'; import 'package:comunic/ui/widgets/network_image_widget.dart';
import 'package:filesize/filesize.dart'; import 'package:filesize/filesize.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher_string.dart';
const _AreaWidth = 150.0; const _AreaWidth = 150.0;
const _AreaHeight = 100.0; const _AreaHeight = 100.0;
@ -124,7 +124,7 @@ class _ConversationFileWidgetState extends State<ConversationFileWidget> {
break; break;
default: default:
launch(file.url!); launchUrlString(file.url!);
} }
} }
} }

View File

@ -6,7 +6,7 @@ import 'package:comunic/utils/intl_utils.dart';
import 'package:comunic/utils/ui_utils.dart'; import 'package:comunic/utils/ui_utils.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher_string.dart';
import 'new_password_input_widget.dart'; import 'new_password_input_widget.dart';
@ -229,7 +229,8 @@ class _CreateAccountWidgetState extends State<CreateAccountWidget> {
widget.onCreated(); widget.onCreated();
} }
void _openTOS() => launch(ServerConfigurationHelper.config!.termsURL); void _openTOS() =>
launchUrlString(ServerConfigurationHelper.config!.termsURL);
void _showCreateAccountError() async { void _showCreateAccountError() async {
await showCupertinoDialog( await showCupertinoDialog(

View File

@ -1,7 +1,7 @@
import 'package:comunic/utils/input_utils.dart'; import 'package:comunic/utils/input_utils.dart';
import 'package:flutter/gestures.dart'; import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher_string.dart';
/// Text rich content widget /// Text rich content widget
/// ///
@ -41,7 +41,7 @@ class TextRichContentWidget extends StatelessWidget {
text: s, text: s,
recognizer: TapGestureRecognizer() recognizer: TapGestureRecognizer()
..onTap = () { ..onTap = () {
launch(s); launchUrlString(s);
})); }));
currString = ""; currString = "";

View File

@ -2,7 +2,7 @@ import 'package:comunic/models/displayed_content.dart';
import 'package:comunic/utils/input_utils.dart'; import 'package:comunic/utils/input_utils.dart';
import 'package:comunic/utils/navigation_utils.dart'; import 'package:comunic/utils/navigation_utils.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher_string.dart';
/// Text widget /// Text widget
/// ///
@ -24,13 +24,14 @@ class TextWidget extends StatelessWidget {
this.textAlign = TextAlign.start, this.textAlign = TextAlign.start,
this.style, this.style,
this.linksColor = Colors.blueAccent, this.linksColor = Colors.blueAccent,
}) : super(key: key); }) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
if (this.content.isNull || this.content.isEmpty) return Text(""); if (this.content.isNull || this.content.isEmpty) return Text("");
var usedStyle = style == null ? Theme.of(context).textTheme.bodyText2 : style; var usedStyle =
style == null ? Theme.of(context).textTheme.bodyText2 : style;
var content = this.content.parsedString!; var content = this.content.parsedString!;
@ -78,7 +79,7 @@ class TextWidget extends StatelessWidget {
word, word,
style: style!.copyWith(color: linksColor), style: style!.copyWith(color: linksColor),
), ),
onTap: () => launch(word), onTap: () => launchUrlString(word),
), ),
), ),
); );

View File

@ -7,14 +7,14 @@ packages:
name: archive name: archive
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "3.2.1" version: "3.3.1"
args: args:
dependency: transitive dependency: transitive
description: description:
name: args name: args
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.3.0" version: "2.3.1"
async: async:
dependency: transitive dependency: transitive
description: description:
@ -35,7 +35,7 @@ packages:
name: cached_network_image name: cached_network_image
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "3.2.0" version: "3.2.1"
cached_network_image_platform_interface: cached_network_image_platform_interface:
dependency: transitive dependency: transitive
description: description:
@ -70,7 +70,7 @@ packages:
name: chewie name: chewie
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.3.0" version: "1.3.4"
chewie_audio: chewie_audio:
dependency: "direct main" dependency: "direct main"
description: description:
@ -98,105 +98,105 @@ packages:
name: collection name: collection
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.15.0" version: "1.16.0"
connectivity_plus: connectivity_plus:
dependency: "direct main" dependency: "direct main"
description: description:
name: connectivity_plus name: connectivity_plus
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.2.1" version: "2.3.6"
connectivity_plus_linux: connectivity_plus_linux:
dependency: transitive dependency: transitive
description: description:
name: connectivity_plus_linux name: connectivity_plus_linux
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.3.0" version: "1.3.1"
connectivity_plus_macos: connectivity_plus_macos:
dependency: transitive dependency: transitive
description: description:
name: connectivity_plus_macos name: connectivity_plus_macos
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.2.1" version: "1.2.4"
connectivity_plus_platform_interface: connectivity_plus_platform_interface:
dependency: transitive dependency: transitive
description: description:
name: connectivity_plus_platform_interface name: connectivity_plus_platform_interface
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.2.0" version: "1.2.1"
connectivity_plus_web: connectivity_plus_web:
dependency: transitive dependency: transitive
description: description:
name: connectivity_plus_web name: connectivity_plus_web
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.2.0" version: "1.2.3"
connectivity_plus_windows: connectivity_plus_windows:
dependency: transitive dependency: transitive
description: description:
name: connectivity_plus_windows name: connectivity_plus_windows
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.2.0" version: "1.2.2"
cross_file: cross_file:
dependency: transitive dependency: transitive
description: description:
name: cross_file name: cross_file
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.3.2" version: "0.3.3+1"
crypto: crypto:
dependency: transitive dependency: transitive
description: description:
name: crypto name: crypto
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "3.0.1" version: "3.0.2"
csslib: csslib:
dependency: transitive dependency: transitive
description: description:
name: csslib name: csslib
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.17.1" version: "0.17.2"
cupertino_icons: cupertino_icons:
dependency: "direct main" dependency: "direct main"
description: description:
name: cupertino_icons name: cupertino_icons
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.0.4" version: "1.0.5"
dart_webrtc: dart_webrtc:
dependency: transitive dependency: transitive
description: description:
name: dart_webrtc name: dart_webrtc
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.0.4" version: "1.0.6"
dbus: dbus:
dependency: transitive dependency: transitive
description: description:
name: dbus name: dbus
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.7.1" version: "0.7.7"
dio: dio:
dependency: "direct main" dependency: "direct main"
description: description:
name: dio name: dio
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "4.0.4" version: "4.0.6"
emoji_picker_flutter: emoji_picker_flutter:
dependency: "direct main" dependency: "direct main"
description: description:
name: emoji_picker_flutter name: emoji_picker_flutter
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.1.2" version: "1.3.0"
event_bus: event_bus:
dependency: "direct main" dependency: "direct main"
description: description:
@ -210,14 +210,14 @@ packages:
name: fake_async name: fake_async
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.2.0" version: "1.3.0"
ffi: ffi:
dependency: transitive dependency: transitive
description: description:
name: ffi name: ffi
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.1.2" version: "2.0.1"
file: file:
dependency: transitive dependency: transitive
description: description:
@ -231,7 +231,7 @@ packages:
name: file_picker name: file_picker
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "4.5.0" version: "5.0.1"
filesize: filesize:
dependency: "direct main" dependency: "direct main"
description: description:
@ -245,42 +245,42 @@ packages:
name: firebase_core name: firebase_core
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.13.1" version: "1.20.0"
firebase_core_platform_interface: firebase_core_platform_interface:
dependency: transitive dependency: transitive
description: description:
name: firebase_core_platform_interface name: firebase_core_platform_interface
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "4.2.5" version: "4.5.0"
firebase_core_web: firebase_core_web:
dependency: transitive dependency: transitive
description: description:
name: firebase_core_web name: firebase_core_web
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.6.1" version: "1.7.1"
firebase_messaging: firebase_messaging:
dependency: "direct main" dependency: "direct main"
description: description:
name: firebase_messaging name: firebase_messaging
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "11.2.8" version: "12.0.1"
firebase_messaging_platform_interface: firebase_messaging_platform_interface:
dependency: transitive dependency: transitive
description: description:
name: firebase_messaging_platform_interface name: firebase_messaging_platform_interface
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "3.2.1" version: "4.1.0"
firebase_messaging_web: firebase_messaging_web:
dependency: transitive dependency: transitive
description: description:
name: firebase_messaging_web name: firebase_messaging_web
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.2.9" version: "3.1.0"
flutter: flutter:
dependency: "direct main" dependency: "direct main"
description: flutter description: flutter
@ -292,7 +292,7 @@ packages:
name: flutter_blurhash name: flutter_blurhash
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.6.4" version: "0.7.0"
flutter_cache_manager: flutter_cache_manager:
dependency: transitive dependency: transitive
description: description:
@ -320,14 +320,14 @@ packages:
name: flutter_launcher_icons name: flutter_launcher_icons
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.9.2" version: "0.9.3"
flutter_plugin_android_lifecycle: flutter_plugin_android_lifecycle:
dependency: transitive dependency: transitive
description: description:
name: flutter_plugin_android_lifecycle name: flutter_plugin_android_lifecycle
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.0.5" version: "2.0.7"
flutter_settings_ui: flutter_settings_ui:
dependency: "direct main" dependency: "direct main"
description: description:
@ -341,7 +341,7 @@ packages:
name: flutter_svg name: flutter_svg
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.0.3" version: "1.1.1+1"
flutter_test: flutter_test:
dependency: "direct dev" dependency: "direct dev"
description: flutter description: flutter
@ -358,7 +358,7 @@ packages:
name: flutter_webrtc name: flutter_webrtc
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.8.3" version: "0.9.1"
html: html:
dependency: "direct main" dependency: "direct main"
description: description:
@ -379,42 +379,70 @@ packages:
name: http_parser name: http_parser
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "4.0.0" version: "4.0.1"
image: image:
dependency: transitive dependency: transitive
description: description:
name: image name: image
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "3.1.3" version: "3.2.0"
image_cropper: image_cropper:
dependency: "direct main" dependency: "direct main"
description: description:
name: image_cropper name: image_cropper
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.5.0" version: "2.0.3"
image_cropper_for_web:
dependency: transitive
description:
name: image_cropper_for_web
url: "https://pub.dartlang.org"
source: hosted
version: "0.0.4"
image_cropper_platform_interface:
dependency: transitive
description:
name: image_cropper_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.0"
image_picker: image_picker:
dependency: "direct main" dependency: "direct main"
description: description:
name: image_picker name: image_picker
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.8.4+10" version: "0.8.5+3"
image_picker_android:
dependency: transitive
description:
name: image_picker_android
url: "https://pub.dartlang.org"
source: hosted
version: "0.8.5+1"
image_picker_for_web: image_picker_for_web:
dependency: transitive dependency: transitive
description: description:
name: image_picker_for_web name: image_picker_for_web
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.1.6" version: "2.1.8"
image_picker_ios:
dependency: transitive
description:
name: image_picker_ios
url: "https://pub.dartlang.org"
source: hosted
version: "0.8.5+6"
image_picker_platform_interface: image_picker_platform_interface:
dependency: transitive dependency: transitive
description: description:
name: image_picker_platform_interface name: image_picker_platform_interface
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.4.4" version: "2.6.0"
intl: intl:
dependency: "direct main" dependency: "direct main"
description: description:
@ -435,7 +463,7 @@ packages:
name: js name: js
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.6.3" version: "0.6.4"
matcher: matcher:
dependency: transitive dependency: transitive
description: description:
@ -449,7 +477,7 @@ packages:
name: material_color_utilities name: material_color_utilities
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.1.3" version: "0.1.4"
meta: meta:
dependency: transitive dependency: transitive
description: description:
@ -463,7 +491,7 @@ packages:
name: mime name: mime
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.0.1" version: "1.0.2"
nested: nested:
dependency: transitive dependency: transitive
description: description:
@ -484,21 +512,21 @@ packages:
name: octo_image name: octo_image
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.0.1" version: "1.0.2"
package_info_plus: package_info_plus:
dependency: "direct main" dependency: "direct main"
description: description:
name: package_info_plus name: package_info_plus
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.4.0" version: "1.4.3"
package_info_plus_linux: package_info_plus_linux:
dependency: transitive dependency: transitive
description: description:
name: package_info_plus_linux name: package_info_plus_linux
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.0.3" version: "1.0.5"
package_info_plus_macos: package_info_plus_macos:
dependency: transitive dependency: transitive
description: description:
@ -519,21 +547,21 @@ packages:
name: package_info_plus_web name: package_info_plus_web
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.0.4" version: "1.0.5"
package_info_plus_windows: package_info_plus_windows:
dependency: transitive dependency: transitive
description: description:
name: package_info_plus_windows name: package_info_plus_windows
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.0.4" version: "2.0.0"
path: path:
dependency: transitive dependency: transitive
description: description:
name: path name: path
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.8.0" version: "1.8.1"
path_drawing: path_drawing:
dependency: transitive dependency: transitive
description: description:
@ -554,49 +582,49 @@ packages:
name: path_provider name: path_provider
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.0.9" version: "2.0.11"
path_provider_android: path_provider_android:
dependency: transitive dependency: transitive
description: description:
name: path_provider_android name: path_provider_android
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.0.12" version: "2.0.17"
path_provider_ios: path_provider_ios:
dependency: transitive dependency: transitive
description: description:
name: path_provider_ios name: path_provider_ios
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.0.8" version: "2.0.11"
path_provider_linux: path_provider_linux:
dependency: transitive dependency: transitive
description: description:
name: path_provider_linux name: path_provider_linux
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.1.5" version: "2.1.7"
path_provider_macos: path_provider_macos:
dependency: transitive dependency: transitive
description: description:
name: path_provider_macos name: path_provider_macos
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.0.5" version: "2.0.6"
path_provider_platform_interface: path_provider_platform_interface:
dependency: transitive dependency: transitive
description: description:
name: path_provider_platform_interface name: path_provider_platform_interface
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.0.3" version: "2.0.4"
path_provider_windows: path_provider_windows:
dependency: transitive dependency: transitive
description: description:
name: path_provider_windows name: path_provider_windows
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.0.5" version: "2.1.0"
pedantic: pedantic:
dependency: transitive dependency: transitive
description: description:
@ -624,7 +652,7 @@ packages:
name: permission_handler_apple name: permission_handler_apple
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "9.0.3" version: "9.0.4"
permission_handler_platform_interface: permission_handler_platform_interface:
dependency: transitive dependency: transitive
description: description:
@ -645,21 +673,21 @@ packages:
name: petitparser name: petitparser
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "4.4.0" version: "5.0.0"
photo_view: photo_view:
dependency: "direct main" dependency: "direct main"
description: description:
name: photo_view name: photo_view
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.13.0" version: "0.14.0"
pie_chart: pie_chart:
dependency: "direct main" dependency: "direct main"
description: description:
name: pie_chart name: pie_chart
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "5.1.0" version: "5.3.2"
platform: platform:
dependency: transitive dependency: transitive
description: description:
@ -687,7 +715,7 @@ packages:
name: provider name: provider
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "6.0.2" version: "6.0.3"
random_string: random_string:
dependency: "direct main" dependency: "direct main"
description: description:
@ -708,42 +736,42 @@ packages:
name: rxdart name: rxdart
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.27.3" version: "0.27.5"
shared_preferences: shared_preferences:
dependency: "direct main" dependency: "direct main"
description: description:
name: shared_preferences name: shared_preferences
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.0.13" version: "2.0.15"
shared_preferences_android: shared_preferences_android:
dependency: transitive dependency: transitive
description: description:
name: shared_preferences_android name: shared_preferences_android
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.0.11" version: "2.0.12"
shared_preferences_ios: shared_preferences_ios:
dependency: transitive dependency: transitive
description: description:
name: shared_preferences_ios name: shared_preferences_ios
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.1.0" version: "2.1.1"
shared_preferences_linux: shared_preferences_linux:
dependency: transitive dependency: transitive
description: description:
name: shared_preferences_linux name: shared_preferences_linux
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.1.0" version: "2.1.1"
shared_preferences_macos: shared_preferences_macos:
dependency: transitive dependency: transitive
description: description:
name: shared_preferences_macos name: shared_preferences_macos
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.0.3" version: "2.0.4"
shared_preferences_platform_interface: shared_preferences_platform_interface:
dependency: transitive dependency: transitive
description: description:
@ -757,14 +785,14 @@ packages:
name: shared_preferences_web name: shared_preferences_web
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.0.3" version: "2.0.4"
shared_preferences_windows: shared_preferences_windows:
dependency: transitive dependency: transitive
description: description:
name: shared_preferences_windows name: shared_preferences_windows
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.1.0" version: "2.1.1"
simple_gesture_detector: simple_gesture_detector:
dependency: transitive dependency: transitive
description: description:
@ -783,21 +811,21 @@ packages:
name: source_span name: source_span
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.8.1" version: "1.8.2"
sqflite: sqflite:
dependency: "direct main" dependency: "direct main"
description: description:
name: sqflite name: sqflite
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.0.2" version: "2.0.3"
sqflite_common: sqflite_common:
dependency: transitive dependency: transitive
description: description:
name: sqflite_common name: sqflite_common
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.2.0" version: "2.2.1+1"
stack_trace: stack_trace:
dependency: transitive dependency: transitive
description: description:
@ -825,14 +853,14 @@ packages:
name: synchronized name: synchronized
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "3.0.0" version: "3.0.0+2"
table_calendar: table_calendar:
dependency: "direct main" dependency: "direct main"
description: description:
name: table_calendar name: table_calendar
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "3.0.4" version: "3.0.6"
term_glyph: term_glyph:
dependency: transitive dependency: transitive
description: description:
@ -846,70 +874,70 @@ packages:
name: test_api name: test_api
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.4.8" version: "0.4.9"
typed_data: typed_data:
dependency: transitive dependency: transitive
description: description:
name: typed_data name: typed_data
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.3.0" version: "1.3.1"
url_launcher: url_launcher:
dependency: "direct main" dependency: "direct main"
description: description:
name: url_launcher name: url_launcher
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "6.0.20" version: "6.1.5"
url_launcher_android: url_launcher_android:
dependency: transitive dependency: transitive
description: description:
name: url_launcher_android name: url_launcher_android
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "6.0.15" version: "6.0.17"
url_launcher_ios: url_launcher_ios:
dependency: transitive dependency: transitive
description: description:
name: url_launcher_ios name: url_launcher_ios
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "6.0.15" version: "6.0.17"
url_launcher_linux: url_launcher_linux:
dependency: transitive dependency: transitive
description: description:
name: url_launcher_linux name: url_launcher_linux
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "3.0.0" version: "3.0.1"
url_launcher_macos: url_launcher_macos:
dependency: transitive dependency: transitive
description: description:
name: url_launcher_macos name: url_launcher_macos
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "3.0.0" version: "3.0.1"
url_launcher_platform_interface: url_launcher_platform_interface:
dependency: transitive dependency: transitive
description: description:
name: url_launcher_platform_interface name: url_launcher_platform_interface
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.0.5" version: "2.1.0"
url_launcher_web: url_launcher_web:
dependency: transitive dependency: transitive
description: description:
name: url_launcher_web name: url_launcher_web
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.0.9" version: "2.0.12"
url_launcher_windows: url_launcher_windows:
dependency: transitive dependency: transitive
description: description:
name: url_launcher_windows name: url_launcher_windows
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "3.0.0" version: "3.0.1"
uuid: uuid:
dependency: transitive dependency: transitive
description: description:
@ -923,70 +951,63 @@ packages:
name: vector_math name: vector_math
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.1.1" version: "2.1.2"
version: version:
dependency: "direct main" dependency: "direct main"
description: description:
name: version name: version
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.0.0" version: "3.0.2"
very_good_analysis:
dependency: transitive
description:
name: very_good_analysis
url: "https://pub.dartlang.org"
source: hosted
version: "2.4.0"
video_player: video_player:
dependency: "direct main" dependency: "direct main"
description: description:
name: video_player name: video_player
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.3.0" version: "2.4.5"
video_player_android: video_player_android:
dependency: transitive dependency: transitive
description: description:
name: video_player_android name: video_player_android
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.3.0" version: "2.3.8"
video_player_avfoundation: video_player_avfoundation:
dependency: transitive dependency: transitive
description: description:
name: video_player_avfoundation name: video_player_avfoundation
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.3.0" version: "2.3.5"
video_player_platform_interface: video_player_platform_interface:
dependency: transitive dependency: transitive
description: description:
name: video_player_platform_interface name: video_player_platform_interface
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "5.1.0" version: "5.1.3"
video_player_web: video_player_web:
dependency: transitive dependency: transitive
description: description:
name: video_player_web name: video_player_web
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.0.7" version: "2.0.12"
video_thumbnail: video_thumbnail:
dependency: "direct main" dependency: "direct main"
description: description:
name: video_thumbnail name: video_thumbnail
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.4.6" version: "0.5.2"
wakelock: wakelock:
dependency: "direct main" dependency: "direct main"
description: description:
name: wakelock name: wakelock
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.6.1+2" version: "0.6.2"
wakelock_macos: wakelock_macos:
dependency: transitive dependency: transitive
description: description:
@ -1021,21 +1042,21 @@ packages:
name: web_socket_channel name: web_socket_channel
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.1.0" version: "2.2.0"
webrtc_interface: webrtc_interface:
dependency: transitive dependency: transitive
description: description:
name: webrtc_interface name: webrtc_interface
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.0.2" version: "1.0.5"
win32: win32:
dependency: transitive dependency: transitive
description: description:
name: win32 name: win32
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.4.1" version: "2.7.0"
xdg_directories: xdg_directories:
dependency: transitive dependency: transitive
description: description:
@ -1049,14 +1070,14 @@ packages:
name: xml name: xml
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "5.3.1" version: "6.1.0"
yaml: yaml:
dependency: transitive dependency: transitive
description: description:
name: yaml name: yaml
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "3.1.0" version: "3.1.1"
sdks: sdks:
dart: ">=2.16.0 <3.0.0" dart: ">=2.17.0 <3.0.0"
flutter: ">=2.10.0" flutter: ">=3.0.0"

View File

@ -11,7 +11,7 @@ description: Comunic client
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
# Read more about iOS versioning at # Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 1.1.11+18 version: 1.2.2+22
environment: environment:
sdk: '>=2.12.0 <3.0.0' sdk: '>=2.12.0 <3.0.0'
@ -68,7 +68,7 @@ dependencies:
random_string: ^2.3.1 random_string: ^2.3.1
# Display zoomable images # Display zoomable images
photo_view: ^0.13.0 photo_view: ^0.14.0
# Check Internet connection # Check Internet connection
connectivity_plus: ^2.2.1 connectivity_plus: ^2.2.1
@ -80,19 +80,19 @@ dependencies:
event_bus: ^2.0.0 event_bus: ^2.0.0
# WebRTC calls # WebRTC calls
flutter_webrtc: ^0.8.3 flutter_webrtc: ^0.9.1
# Prevent phone from auto-locking during calls # Prevent phone from auto-locking during calls
wakelock: ^0.6.1+2 wakelock: ^0.6.1+2
# Pick any kind of file # Pick any kind of file
file_picker: ^4.5.0 file_picker: ^5.0.1
# Get information about current version # Get information about current version
package_info_plus: ^1.4.0 package_info_plus: ^1.4.0
# Version manager # Version manager
version: ^2.0.0 version: ^3.0.2
# Get path to temporary files # Get path to temporary files
path_provider: ^2.0.9 path_provider: ^2.0.9
@ -104,7 +104,7 @@ dependencies:
clipboard: ^0.1.3 clipboard: ^0.1.3
# Video / Audio player # Video / Audio player
video_player: ^2.3.0 video_player: ^2.4.3
chewie_audio: ^1.3.0 chewie_audio: ^1.3.0
chewie: ^1.3.0 chewie: ^1.3.0
@ -112,7 +112,7 @@ dependencies:
mime: ^1.0.1 mime: ^1.0.1
# Create video thumbnails # Create video thumbnails
video_thumbnail: ^0.4.6 video_thumbnail: ^0.5.0
# Record audio file # Record audio file
record_mp3: ^3.0.0 record_mp3: ^3.0.0
@ -127,11 +127,11 @@ dependencies:
flutter_colorpicker: ^1.0.3 flutter_colorpicker: ^1.0.3
# Image cropper # Image cropper
image_cropper: ^1.5.0 image_cropper: ^2.0.3
# Firebase cloud messaging (for push notifications) # Firebase cloud messaging (for push notifications)
firebase_core: ^1.13.1 firebase_core: ^1.13.1
firebase_messaging: ^11.2.8 firebase_messaging: ^12.0.1
# Forez presence # Forez presence
table_calendar: ^3.0.4 table_calendar: ^3.0.4