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 {
compileSdkVersion 32
compileSdkVersion flutter.compileSdkVersion
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
@ -59,8 +59,8 @@ android {
defaultConfig {
applicationId "org.communiquons.comunic"
minSdkVersion 21
targetSdkVersion 32
minSdkVersion 23
targetSdkVersion flutter.targetSdkVersion
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

View File

@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
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 member!": "Echec de la suppression d'un membre !",
"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 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 !",
@ -500,6 +501,7 @@
"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 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 enter message content: ": "Veuillez entrer le contenu du message :",
"Please enter new message content:": "Veuillez entrer le contenu du nouveau message :",
@ -534,6 +536,7 @@
"Question 1": "Question 1",
"Question 2": "Question 2",
"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",
"Record audio": "Faire un enregistrement audio",
"Recording...": "Enregistrement...",
@ -543,6 +546,8 @@
"Remove": "Supprimer",
"Remove selected image": "Supprimer l'image sélectionnée",
"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",
"Requested": "En attente",
"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!\\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 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 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 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",

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/ui_utils.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
///
@ -165,7 +165,7 @@ class _ForezMemberProfileRouteState extends State<ForezMemberProfileRoute> {
subtitle: Text(tr("Website")!),
trailing: IconButton(
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: lines_longer_than_80_chars
// ignore_for_file: depend_on_referenced_packages
import 'package:connectivity_plus_web/connectivity_plus_web.dart';
import 'package:file_picker/_internal/file_picker_web.dart';
import 'package:firebase_core_web/firebase_core_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:package_info_plus_web/package_info_plus_web.dart';
import 'package:shared_preferences_web/shared_preferences_web.dart';
@ -24,6 +26,7 @@ void registerPlugins(Registrar registrar) {
FilePickerWeb.registerWith(registrar);
FirebaseCoreWeb.registerWith(registrar);
FirebaseMessagingWeb.registerWith(registrar);
ImageCropperPlugin.registerWith(registrar);
ImagePickerPlugin.registerWith(registrar);
PackageInfoPlugin.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,6 +23,7 @@ class ServerConfigurationHelper {
final dataConservationPolicy = response["data_conservation_policy"];
final conversationsPolicy = response["conversations_policy"];
final accountInformationPolicy = response["account_info_policy"];
final reportPolicy = response["report_policy"];
_config = ServerConfig(
minSupportedMobileVersion:
@ -80,7 +81,8 @@ class ServerConfigurationHelper {
writingEventInterval: conversationsPolicy["writing_event_interval"],
writingEventLifetime: conversationsPolicy["writing_event_lifetime"],
maxMessageImageWidth: conversationsPolicy["max_message_image_width"],
maxMessageImageHeight: conversationsPolicy["max_message_image_height"],
maxMessageImageHeight:
conversationsPolicy["max_message_image_height"],
maxThumbnailWidth: conversationsPolicy["max_thumbnail_width"],
maxThumbnailHeight: conversationsPolicy["max_thumbnail_height"],
maxLogoWidth: conversationsPolicy["max_logo_width"],
@ -93,7 +95,18 @@ class ServerConfigurationHelper {
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

View File

@ -6,6 +6,7 @@ import 'package:comunic/lists/comments_list.dart';
import 'package:comunic/models/displayed_content.dart';
import 'package:comunic/models/like_element.dart';
import 'package:comunic/models/survey.dart';
import 'package:comunic/utils/account_utils.dart' as account;
/// Single post information
///
@ -76,6 +77,8 @@ class Post implements LikeElement {
access == UserAccessLevels.FULL ||
access == UserAccessLevels.INTERMEDIATE;
bool get isOwner => userID == account.userID();
@override
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/intl_utils.dart';
import 'package:version/version.dart';
/// Server static configuration
@ -137,6 +138,28 @@ class Banner {
bool get visible => enabled && (expire == null || expire! > time());
}
class ReportCause {
final String id;
final Map<String, String> label;
const ReportCause({required this.id, required this.label});
String get localeLabel =>
label.containsKey(shortLang) ? label[shortLang]! : label["en"]!;
}
class ReportPolicy {
final List<ReportCause> causes;
final int maxCommentLength;
final bool canUserReportHisOwnContent;
const ReportPolicy({
required this.causes,
required this.maxCommentLength,
required this.canUserReportHisOwnContent,
});
}
class ServerConfig {
final Version minSupportedMobileVersion;
final String termsURL;
@ -150,6 +173,7 @@ class ServerConfig {
final ServerDataConservationPolicy dataConservationPolicy;
final ConversationsPolicy conversationsPolicy;
final AccountInformationPolicy accountInformationPolicy;
final ReportPolicy? reportPolicy;
const ServerConfig({
required this.minSupportedMobileVersion,
@ -164,5 +188,8 @@ class ServerConfig {
required this.dataConservationPolicy,
required this.conversationsPolicy,
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/utils/intl_utils.dart';
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:url_launcher/url_launcher_string.dart';
/// Deprecation dialog
///
@ -22,12 +22,12 @@ class _DeprecationDialog extends StatelessWidget {
actions: [
MaterialButton(
onPressed: () =>
launch(ServerConfigurationHelper.config!.playStoreURL),
launchUrlString(ServerConfigurationHelper.config!.playStoreURL),
child: Text(tr("Go to the Play Store")!),
),
MaterialButton(
onPressed: () =>
launch(ServerConfigurationHelper.config!.androidDirectDownloadURL),
onPressed: () => launchUrlString(
ServerConfigurationHelper.config!.androidDirectDownloadURL),
child: Text(tr("Download update outside Play Store")!),
),
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:flutter/material.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
///
@ -26,7 +26,7 @@ class _FullScreenImageRouteState extends State<FullScreenImageRoute> {
title: Text(tr("Image")!),
actions: [
IconButton(
icon: Icon(Icons.launch), onPressed: () => launch(widget.url))
icon: Icon(Icons.launch), onPressed: () => launchUrlString(widget.url))
],
),
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
Future<BytesFile> showImageCropper(BuildContext context, BytesFile source,
{CropAspectRatio? aspectRatio}) async {
File? file;
File? cropped;
@ -27,15 +26,18 @@ Future<BytesFile> showImageCropper(BuildContext context, BytesFile source,
file = await generateTemporaryFile();
await file.writeAsBytes(source.bytes!);
File? cropped = await ImageCropper().cropImage(
var cropped = await ImageCropper().cropImage(
sourcePath: file.absolute.path,
compressFormat: ImageCompressFormat.png,
aspectRatio: aspectRatio,
androidUiSettings: AndroidUiSettings(
uiSettings: [
AndroidUiSettings(
toolbarColor: Colors.black,
toolbarTitle: tr("Crop Photo"),
toolbarWidgetColor: Colors.white,
),
IOSUiSettings(title: tr("Crop Photo"))
],
);
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:flutter/material.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
///
@ -24,11 +24,11 @@ class AboutApplicationSettings extends StatelessWidget {
tiles: [
SettingsTile(
title: tr("Privacy policy"),
onPressed: (c) => launch(srvConfig!.privacyPolicyURL),
onPressed: (c) => launchUrlString(srvConfig!.privacyPolicyURL),
),
SettingsTile(
title: tr("Terms of Use"),
onPressed: (c) => launch(srvConfig!.termsURL),
onPressed: (c) => launchUrlString(srvConfig!.termsURL),
),
SettingsTile(
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/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/screens/group_sections/about_group_section.dart';
import 'package:comunic/ui/screens/group_sections/forez_presence_section.dart';
@ -65,7 +69,10 @@ class _AuthorizedGroupPageScreenState
// About the group
_GroupPageTab(
widget: (c) => AboutGroupSection(group: _group),
widget: (c) => AboutGroupSection(
group: _group,
onReportGroup: _reportGroup,
),
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 {

View File

@ -1,5 +1,6 @@
import 'dart:async';
import 'package:comunic/enums/report_target_type.dart';
import 'package:comunic/helpers/conversations_helper.dart';
import 'package:comunic/helpers/events_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_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/report_dialog.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/server_conversation_message_tile.dart';
@ -504,6 +507,7 @@ class _ConversationScreenState extends SafeState<ConversationScreen> {
onRequestMessageStats: _requestMessageStats,
onRequestMessageUpdate: _updateMessage,
onRequestMessageDelete: _deleteMessage,
onReportMessage: _reportMessage,
);
Widget _buildDateWidget(DateTime dt) => Center(
@ -753,4 +757,9 @@ class _ConversationScreenState extends SafeState<ConversationScreen> {
if (!await _conversationsHelper.deleteMessage(message.id))
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 '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/events_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/users_list.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/screens/create_conversation_screen.dart';
import 'package:comunic/ui/tiles/conversation_tile.dart';
@ -160,6 +163,11 @@ class _ConversationScreenState extends SafeState<ConversationsListScreen> {
_loadConversationsList(false);
}
/// Handle conversation report request
void _reportConversation(Conversation conversation) => showReportDialog(
ctx: context,
target: ReportTarget(ReportTargetType.Conversation, conversation.id!));
@override
Widget build(BuildContext context) {
if (_error == LoadErrorLevel.MAJOR) return _buildErrorCard();
@ -201,6 +209,7 @@ class _ConversationScreenState extends SafeState<ConversationsListScreen> {
},
onRequestUpdate: _updateConversation,
onRequestLeave: _requestLeaveConversation,
onReport: _reportConversation,
);
},
itemCount: max(_list!.length, 1),

View File

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

View File

@ -1,9 +1,10 @@
import 'package:comunic/helpers/server_config_helper.dart';
import 'package:comunic/models/advanced_group_info.dart';
import 'package:comunic/models/group.dart';
import 'package:comunic/utils/date_utils.dart';
import 'package:comunic/utils/intl_utils.dart';
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:url_launcher/url_launcher_string.dart';
/// About group section
///
@ -11,10 +12,12 @@ import 'package:url_launcher/url_launcher.dart';
class AboutGroupSection extends StatelessWidget {
final AdvancedGroupInfo group;
final Function(Group) onReportGroup;
const AboutGroupSection({
Key? key,
required this.group,
required this.onReportGroup,
}) : super(key: key);
@override
@ -26,7 +29,7 @@ class AboutGroupSection extends StatelessWidget {
leading: Icon(Icons.link),
title: Text(tr("URL")!),
subtitle: Text(group.url),
onTap: () => launch(group.url),
onTap: () => launchUrlString(group.url),
)
: Container(),
@ -103,6 +106,15 @@ class AboutGroupSection extends StatelessWidget {
subtitle: Text(tr("Forez special features enabled")!),
)
: 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/server_config_helper.dart';
import 'package:comunic/lists/groups_list.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/widgets/group_icon_widget.dart';
import 'package:comunic/ui/widgets/group_membership_widget.dart';
@ -70,9 +74,26 @@ class _GroupsListScreenState extends SafeState<GroupsListScreen> {
group: g,
onUpdated: () => _refreshIndicatorKey.currentState!.show(),
),
trailing: IconButton(
trailing: IntrinsicWidth(
child: Row(
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),
))
.toList(),
@ -122,6 +143,10 @@ class _GroupsListScreenState extends SafeState<GroupsListScreen> {
_refreshIndicatorKey.currentState!.show();
}
/// Report a group
void _reportGroup(Group g) => showReportDialog(
ctx: context, target: ReportTarget(ReportTargetType.Group, g.id));
/// Add a group
void _createGroup() async {
try {

View File

@ -20,8 +20,7 @@ enum _PageStatus { LOADING, ERROR, READY }
class UserPageScreen extends StatefulWidget {
final int userID;
const UserPageScreen({Key? key, required this.userID})
: super(key: key);
const UserPageScreen({Key? key, required this.userID}) : super(key: key);
@override
_UserPageScreenState createState() => _UserPageScreenState();
@ -42,7 +41,7 @@ class _UserPageScreenState extends SafeState<UserPageScreen> {
@override
void didChangeDependencies() {
super.didChangeDependencies();
if(_userInfo?.id != widget.userID)
if (_userInfo?.id == widget.userID) return;
_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/helpers/friends_helper.dart';
import 'package:comunic/helpers/server_config_helper.dart';
import 'package:comunic/models/advanced_user_info.dart';
import 'package:comunic/models/displayed_content.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/async_screen_widget.dart';
import 'package:comunic/ui/widgets/text_widget.dart';
@ -13,7 +17,7 @@ import 'package:comunic/utils/intl_utils.dart';
///
/// @author Pierre Hubert
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:url_launcher/url_launcher_string.dart';
class AboutUserSection extends StatefulWidget {
final AdvancedUserInfo user;
@ -59,7 +63,7 @@ class _AboutUserSectionState extends State<AboutUserSection> {
leading: Icon(Icons.link),
title: Text(tr("Personal Website")!),
subtitle: Text(widget.user.personalWebsite),
onTap: () => launch(widget.user.personalWebsite),
onTap: () => launchUrlString(widget.user.personalWebsite),
)
: Container(),
@ -129,6 +133,23 @@ class _AboutUserSectionState extends State<AboutUserSection> {
? tr("Public 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/user.dart';
import 'package:comunic/ui/widgets/account_image_widget.dart';
@ -10,11 +11,17 @@ import 'package:flutter/material.dart';
///
/// @author Pierre HUBERT
enum _FriendMenuChoices { REMOVE, TOGGLE_FOLLOWING, PRIVATE_CONVERSATION }
enum _FriendMenuChoices {
REMOVE,
TOGGLE_FOLLOWING,
PRIVATE_CONVERSATION,
REPORT
}
typedef OnRequestDeleteFriend = void Function(Friend);
typedef OnSetFollowing = void Function(Friend, bool);
typedef OnOpenPrivateConversation = void Function(Friend);
typedef OnReportFriend = void Function(Friend);
class AcceptedFriendTile extends StatelessWidget {
final Friend friend;
@ -22,6 +29,7 @@ class AcceptedFriendTile extends StatelessWidget {
final OnRequestDeleteFriend onRequestDelete;
final OnSetFollowing onSetFollowing;
final OnOpenPrivateConversation onOpenPrivateConversation;
final OnReportFriend onReportFriend;
const AcceptedFriendTile({
Key? key,
@ -30,6 +38,7 @@ class AcceptedFriendTile extends StatelessWidget {
required this.onRequestDelete,
required this.onSetFollowing,
required this.onOpenPrivateConversation,
required this.onReportFriend,
}) : super(key: key);
@override
@ -75,7 +84,15 @@ class AcceptedFriendTile extends StatelessWidget {
child: Text(tr("Remove")!),
value: _FriendMenuChoices.REMOVE,
),
],
]..addAll(srvConfig!.isReportingEnabled
? [
// Report user
PopupMenuItem(
child: Text(tr("Report abuse")!),
value: _FriendMenuChoices.REPORT,
),
]
: []),
onSelected: _selectedMenuOption,
),
);
@ -97,6 +114,10 @@ class AcceptedFriendTile extends StatelessWidget {
case _FriendMenuChoices.REMOVE:
onRequestDelete(friend);
break;
case _FriendMenuChoices.REPORT:
onReportFriend(friend);
break;
}
}
}

View File

@ -1,3 +1,4 @@
import 'package:comunic/helpers/server_config_helper.dart';
import 'package:comunic/models/comment.dart';
import 'package:comunic/models/user.dart';
import 'package:comunic/ui/widgets/account_image_widget.dart';
@ -13,13 +14,14 @@ import 'package:flutter/material.dart';
///
/// @author Pierre HUBERT
enum _CommentAction { DELETE, UPDATE }
enum _CommentAction { DELETE, UPDATE, REPORT }
class CommentTile extends StatelessWidget {
final Comment comment;
final User user;
final void Function(Comment) onUpdateComment;
final void Function(Comment) onDeleteComment;
final void Function(Comment) onReportComment;
const CommentTile({
Key? key,
@ -27,6 +29,7 @@ class CommentTile extends StatelessWidget {
required this.user,
required this.onUpdateComment,
required this.onDeleteComment,
required this.onReportComment,
}) : super(key: key);
@override
@ -37,34 +40,13 @@ class CommentTile extends StatelessWidget {
user.displayName,
),
subtitle: _buildCommentContent(),
trailing: Text(
diffTimeFromNowToStr(comment.timeSent)!,
style: TextStyle(fontSize: 10.0),
),
trailing: _buildTrailing(),
);
}
Widget _buildAccountImageWidget() {
return PopupMenuButton<_CommentAction>(
child: AccountImageWidget(
return AccountImageWidget(
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
void _selectedMenuOption(_CommentAction value) {
switch (value) {
@ -113,6 +140,11 @@ class CommentTile extends StatelessWidget {
case _CommentAction.DELETE:
onDeleteComment(comment);
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/user.dart';
import 'package:comunic/ui/widgets/conversation_file_tile.dart';
@ -17,6 +18,7 @@ enum _MenuChoices {
DELETE,
REQUEST_UPDATE_CONTENT,
GET_STATS,
REPORT
}
typedef OnRequestMessageStats = void Function(ConversationMessage);
@ -29,6 +31,7 @@ class ConversationMessageTile extends StatelessWidget {
final OnRequestMessageStats onRequestMessageStats;
final OnRequestMessageUpdate onRequestMessageUpdate;
final OnRequestMessageDelete onRequestMessageDelete;
final Function(ConversationMessage) onReportMessage;
const ConversationMessageTile({
Key? key,
@ -37,6 +40,7 @@ class ConversationMessageTile extends StatelessWidget {
required this.onRequestMessageStats,
required this.onRequestMessageUpdate,
required this.onRequestMessageDelete,
required this.onReportMessage,
}) : super(key: key);
@override
@ -90,6 +94,16 @@ class ConversationMessageTile extends StatelessWidget {
value: _MenuChoices.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),
),
)
@ -133,6 +147,10 @@ class ConversationMessageTile extends StatelessWidget {
case _MenuChoices.DELETE:
onRequestMessageDelete(message);
break;
case _MenuChoices.REPORT:
onReportMessage(message);
break;
}
}
}

View File

@ -1,4 +1,5 @@
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/users_list.dart';
import 'package:comunic/models/config.dart';
@ -18,7 +19,7 @@ typedef OpenConversationCallback = void Function(Conversation);
typedef RequestLeaveConversationCallback = void Function(Conversation);
typedef RequestUpdateConversationCallback = void Function(Conversation);
enum _PopupMenuChoices { UPDATE, LEAVE }
enum _PopupMenuChoices { UPDATE, LEAVE, REPORT }
class ConversationTile extends StatelessWidget {
final Conversation conversation;
@ -27,6 +28,7 @@ class ConversationTile extends StatelessWidget {
final OpenConversationCallback onOpen;
final RequestUpdateConversationCallback onRequestUpdate;
final RequestLeaveConversationCallback onRequestLeave;
final Function(Conversation) onReport;
const ConversationTile({
Key? key,
@ -36,6 +38,7 @@ class ConversationTile extends StatelessWidget {
required this.onOpen,
required this.onRequestUpdate,
required this.onRequestLeave,
required this.onReport,
}) : super(key: key);
_buildSubInformation(IconData icon, String content) {
@ -127,7 +130,15 @@ class ConversationTile extends StatelessWidget {
child: Text(tr("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);
break;
default:
case _PopupMenuChoices.REPORT:
onReport(conversation);
break;
case null:
break;
}
}

View File

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

View File

@ -2,15 +2,19 @@ import 'dart:math';
import 'package:comunic/enums/post_kind.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/posts_helper.dart';
import 'package:comunic/helpers/server_config_helper.dart';
import 'package:comunic/lists/groups_list.dart';
import 'package:comunic/lists/users_list.dart';
import 'package:comunic/models/comment.dart';
import 'package:comunic/models/new_comment.dart';
import 'package:comunic/models/post.dart';
import 'package:comunic/models/report_target.dart';
import 'package:comunic/models/user.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/widgets/account_image_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/ui_utils.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 '../../utils/log_utils.dart';
@ -41,7 +45,7 @@ const TextStyle _userNameStyle = TextStyle(
fontSize: 16.0);
/// Post actions
enum _PostActions { DELETE, UPDATE_CONTENT }
enum _PostActions { DELETE, UPDATE_CONTENT, REPORT }
class PostTile extends StatefulWidget {
final Post post;
@ -152,7 +156,16 @@ class _PostTileState extends State<PostTile> {
value: _PostActions.DELETE,
enabled: widget.post.canDelete,
),
],
]..addAll(srvConfig!.isReportingEnabled &&
(!widget.post.isOwner ||
srvConfig!.reportPolicy!.canUserReportHisOwnContent)
? [
PopupMenuItem(
child: Text(tr("Report abuse")!),
value: _PostActions.REPORT,
)
]
: []),
onSelected: _selectedPostMenuAction,
)
],
@ -270,8 +283,8 @@ class _PostTileState extends State<PostTile> {
Text(tr("YouTube movie")!)
],
),
onPressed: () =>
launch("https://youtube.com/watch/?v=" + widget.post.filePath!),
onPressed: () => launchUrlString(
"https://youtube.com/watch/?v=" + widget.post.filePath!),
);
}
@ -280,7 +293,7 @@ class _PostTileState extends State<PostTile> {
color:
darkTheme() ? darkerAccentColor : Color.fromRGBO(0xf7, 0xf7, 0xf7, 1),
child: InkWell(
onTap: () => launch(widget.post.linkURL!),
onTap: () => launchUrlString(widget.post.linkURL!),
child: Row(
children: <Widget>[
Padding(
@ -319,7 +332,7 @@ class _PostTileState extends State<PostTile> {
Widget _buildPostPDF() {
return ElevatedButton.icon(
onPressed: () {
launch(widget.post.fileURL!);
launchUrlString(widget.post.fileURL!);
},
icon: Icon(Icons.picture_as_pdf),
label: Text(tr("PDF")!),
@ -356,6 +369,7 @@ class _PostTileState extends State<PostTile> {
user: widget.usersInfo.getUser(comment.userID),
onUpdateComment: _updateCommentContent,
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
void _selectedPostMenuAction(_PostActions value) {
switch (value) {
@ -561,6 +579,11 @@ class _PostTileState extends State<PostTile> {
case _PostActions.DELETE:
confirmDelete();
break;
// Report content
case _PostActions.REPORT:
reportContent();
break;
}
}
@ -620,4 +643,9 @@ class _PostTileState extends State<PostTile> {
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/intl_utils.dart';
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:url_launcher/url_launcher_string.dart';
bool _bannerDismissed = false;
@ -26,7 +26,7 @@ class _BannerWidgetState extends State<BannerWidget> {
}
void _openLink() {
launch(srvConfig!.banner!.link!);
launchUrlString(srvConfig!.banner!.link!);
}
@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:filesize/filesize.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 _AreaHeight = 100.0;
@ -124,7 +124,7 @@ class _ConversationFileWidgetState extends State<ConversationFileWidget> {
break;
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:flutter/cupertino.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';
@ -229,7 +229,8 @@ class _CreateAccountWidgetState extends State<CreateAccountWidget> {
widget.onCreated();
}
void _openTOS() => launch(ServerConfigurationHelper.config!.termsURL);
void _openTOS() =>
launchUrlString(ServerConfigurationHelper.config!.termsURL);
void _showCreateAccountError() async {
await showCupertinoDialog(

View File

@ -1,7 +1,7 @@
import 'package:comunic/utils/input_utils.dart';
import 'package:flutter/gestures.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
///
@ -41,7 +41,7 @@ class TextRichContentWidget extends StatelessWidget {
text: s,
recognizer: TapGestureRecognizer()
..onTap = () {
launch(s);
launchUrlString(s);
}));
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/navigation_utils.dart';
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:url_launcher/url_launcher_string.dart';
/// Text widget
///
@ -30,7 +30,8 @@ class TextWidget extends StatelessWidget {
Widget build(BuildContext context) {
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!;
@ -78,7 +79,7 @@ class TextWidget extends StatelessWidget {
word,
style: style!.copyWith(color: linksColor),
),
onTap: () => launch(word),
onTap: () => launchUrlString(word),
),
),
);

View File

@ -7,14 +7,14 @@ packages:
name: archive
url: "https://pub.dartlang.org"
source: hosted
version: "3.2.1"
version: "3.3.1"
args:
dependency: transitive
description:
name: args
url: "https://pub.dartlang.org"
source: hosted
version: "2.3.0"
version: "2.3.1"
async:
dependency: transitive
description:
@ -35,7 +35,7 @@ packages:
name: cached_network_image
url: "https://pub.dartlang.org"
source: hosted
version: "3.2.0"
version: "3.2.1"
cached_network_image_platform_interface:
dependency: transitive
description:
@ -70,7 +70,7 @@ packages:
name: chewie
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.0"
version: "1.3.4"
chewie_audio:
dependency: "direct main"
description:
@ -98,105 +98,105 @@ packages:
name: collection
url: "https://pub.dartlang.org"
source: hosted
version: "1.15.0"
version: "1.16.0"
connectivity_plus:
dependency: "direct main"
description:
name: connectivity_plus
url: "https://pub.dartlang.org"
source: hosted
version: "2.2.1"
version: "2.3.6"
connectivity_plus_linux:
dependency: transitive
description:
name: connectivity_plus_linux
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.0"
version: "1.3.1"
connectivity_plus_macos:
dependency: transitive
description:
name: connectivity_plus_macos
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.1"
version: "1.2.4"
connectivity_plus_platform_interface:
dependency: transitive
description:
name: connectivity_plus_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0"
version: "1.2.1"
connectivity_plus_web:
dependency: transitive
description:
name: connectivity_plus_web
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0"
version: "1.2.3"
connectivity_plus_windows:
dependency: transitive
description:
name: connectivity_plus_windows
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0"
version: "1.2.2"
cross_file:
dependency: transitive
description:
name: cross_file
url: "https://pub.dartlang.org"
source: hosted
version: "0.3.2"
version: "0.3.3+1"
crypto:
dependency: transitive
description:
name: crypto
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.1"
version: "3.0.2"
csslib:
dependency: transitive
description:
name: csslib
url: "https://pub.dartlang.org"
source: hosted
version: "0.17.1"
version: "0.17.2"
cupertino_icons:
dependency: "direct main"
description:
name: cupertino_icons
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.4"
version: "1.0.5"
dart_webrtc:
dependency: transitive
description:
name: dart_webrtc
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.4"
version: "1.0.6"
dbus:
dependency: transitive
description:
name: dbus
url: "https://pub.dartlang.org"
source: hosted
version: "0.7.1"
version: "0.7.7"
dio:
dependency: "direct main"
description:
name: dio
url: "https://pub.dartlang.org"
source: hosted
version: "4.0.4"
version: "4.0.6"
emoji_picker_flutter:
dependency: "direct main"
description:
name: emoji_picker_flutter
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.2"
version: "1.3.0"
event_bus:
dependency: "direct main"
description:
@ -210,14 +210,14 @@ packages:
name: fake_async
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0"
version: "1.3.0"
ffi:
dependency: transitive
description:
name: ffi
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.2"
version: "2.0.1"
file:
dependency: transitive
description:
@ -231,7 +231,7 @@ packages:
name: file_picker
url: "https://pub.dartlang.org"
source: hosted
version: "4.5.0"
version: "5.0.1"
filesize:
dependency: "direct main"
description:
@ -245,42 +245,42 @@ packages:
name: firebase_core
url: "https://pub.dartlang.org"
source: hosted
version: "1.13.1"
version: "1.20.0"
firebase_core_platform_interface:
dependency: transitive
description:
name: firebase_core_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "4.2.5"
version: "4.5.0"
firebase_core_web:
dependency: transitive
description:
name: firebase_core_web
url: "https://pub.dartlang.org"
source: hosted
version: "1.6.1"
version: "1.7.1"
firebase_messaging:
dependency: "direct main"
description:
name: firebase_messaging
url: "https://pub.dartlang.org"
source: hosted
version: "11.2.8"
version: "12.0.1"
firebase_messaging_platform_interface:
dependency: transitive
description:
name: firebase_messaging_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "3.2.1"
version: "4.1.0"
firebase_messaging_web:
dependency: transitive
description:
name: firebase_messaging_web
url: "https://pub.dartlang.org"
source: hosted
version: "2.2.9"
version: "3.1.0"
flutter:
dependency: "direct main"
description: flutter
@ -292,7 +292,7 @@ packages:
name: flutter_blurhash
url: "https://pub.dartlang.org"
source: hosted
version: "0.6.4"
version: "0.7.0"
flutter_cache_manager:
dependency: transitive
description:
@ -320,14 +320,14 @@ packages:
name: flutter_launcher_icons
url: "https://pub.dartlang.org"
source: hosted
version: "0.9.2"
version: "0.9.3"
flutter_plugin_android_lifecycle:
dependency: transitive
description:
name: flutter_plugin_android_lifecycle
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.5"
version: "2.0.7"
flutter_settings_ui:
dependency: "direct main"
description:
@ -341,7 +341,7 @@ packages:
name: flutter_svg
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.3"
version: "1.1.1+1"
flutter_test:
dependency: "direct dev"
description: flutter
@ -358,7 +358,7 @@ packages:
name: flutter_webrtc
url: "https://pub.dartlang.org"
source: hosted
version: "0.8.3"
version: "0.9.1"
html:
dependency: "direct main"
description:
@ -379,42 +379,70 @@ packages:
name: http_parser
url: "https://pub.dartlang.org"
source: hosted
version: "4.0.0"
version: "4.0.1"
image:
dependency: transitive
description:
name: image
url: "https://pub.dartlang.org"
source: hosted
version: "3.1.3"
version: "3.2.0"
image_cropper:
dependency: "direct main"
description:
name: image_cropper
url: "https://pub.dartlang.org"
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:
dependency: "direct main"
description:
name: image_picker
url: "https://pub.dartlang.org"
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:
dependency: transitive
description:
name: image_picker_for_web
url: "https://pub.dartlang.org"
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:
dependency: transitive
description:
name: image_picker_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "2.4.4"
version: "2.6.0"
intl:
dependency: "direct main"
description:
@ -435,7 +463,7 @@ packages:
name: js
url: "https://pub.dartlang.org"
source: hosted
version: "0.6.3"
version: "0.6.4"
matcher:
dependency: transitive
description:
@ -449,7 +477,7 @@ packages:
name: material_color_utilities
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.3"
version: "0.1.4"
meta:
dependency: transitive
description:
@ -463,7 +491,7 @@ packages:
name: mime
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.1"
version: "1.0.2"
nested:
dependency: transitive
description:
@ -484,21 +512,21 @@ packages:
name: octo_image
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.1"
version: "1.0.2"
package_info_plus:
dependency: "direct main"
description:
name: package_info_plus
url: "https://pub.dartlang.org"
source: hosted
version: "1.4.0"
version: "1.4.3"
package_info_plus_linux:
dependency: transitive
description:
name: package_info_plus_linux
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.3"
version: "1.0.5"
package_info_plus_macos:
dependency: transitive
description:
@ -519,21 +547,21 @@ packages:
name: package_info_plus_web
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.4"
version: "1.0.5"
package_info_plus_windows:
dependency: transitive
description:
name: package_info_plus_windows
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.4"
version: "2.0.0"
path:
dependency: transitive
description:
name: path
url: "https://pub.dartlang.org"
source: hosted
version: "1.8.0"
version: "1.8.1"
path_drawing:
dependency: transitive
description:
@ -554,49 +582,49 @@ packages:
name: path_provider
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.9"
version: "2.0.11"
path_provider_android:
dependency: transitive
description:
name: path_provider_android
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.12"
version: "2.0.17"
path_provider_ios:
dependency: transitive
description:
name: path_provider_ios
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.8"
version: "2.0.11"
path_provider_linux:
dependency: transitive
description:
name: path_provider_linux
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.5"
version: "2.1.7"
path_provider_macos:
dependency: transitive
description:
name: path_provider_macos
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.5"
version: "2.0.6"
path_provider_platform_interface:
dependency: transitive
description:
name: path_provider_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.3"
version: "2.0.4"
path_provider_windows:
dependency: transitive
description:
name: path_provider_windows
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.5"
version: "2.1.0"
pedantic:
dependency: transitive
description:
@ -624,7 +652,7 @@ packages:
name: permission_handler_apple
url: "https://pub.dartlang.org"
source: hosted
version: "9.0.3"
version: "9.0.4"
permission_handler_platform_interface:
dependency: transitive
description:
@ -645,21 +673,21 @@ packages:
name: petitparser
url: "https://pub.dartlang.org"
source: hosted
version: "4.4.0"
version: "5.0.0"
photo_view:
dependency: "direct main"
description:
name: photo_view
url: "https://pub.dartlang.org"
source: hosted
version: "0.13.0"
version: "0.14.0"
pie_chart:
dependency: "direct main"
description:
name: pie_chart
url: "https://pub.dartlang.org"
source: hosted
version: "5.1.0"
version: "5.3.2"
platform:
dependency: transitive
description:
@ -687,7 +715,7 @@ packages:
name: provider
url: "https://pub.dartlang.org"
source: hosted
version: "6.0.2"
version: "6.0.3"
random_string:
dependency: "direct main"
description:
@ -708,42 +736,42 @@ packages:
name: rxdart
url: "https://pub.dartlang.org"
source: hosted
version: "0.27.3"
version: "0.27.5"
shared_preferences:
dependency: "direct main"
description:
name: shared_preferences
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.13"
version: "2.0.15"
shared_preferences_android:
dependency: transitive
description:
name: shared_preferences_android
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.11"
version: "2.0.12"
shared_preferences_ios:
dependency: transitive
description:
name: shared_preferences_ios
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
version: "2.1.1"
shared_preferences_linux:
dependency: transitive
description:
name: shared_preferences_linux
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
version: "2.1.1"
shared_preferences_macos:
dependency: transitive
description:
name: shared_preferences_macos
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.3"
version: "2.0.4"
shared_preferences_platform_interface:
dependency: transitive
description:
@ -757,14 +785,14 @@ packages:
name: shared_preferences_web
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.3"
version: "2.0.4"
shared_preferences_windows:
dependency: transitive
description:
name: shared_preferences_windows
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
version: "2.1.1"
simple_gesture_detector:
dependency: transitive
description:
@ -783,21 +811,21 @@ packages:
name: source_span
url: "https://pub.dartlang.org"
source: hosted
version: "1.8.1"
version: "1.8.2"
sqflite:
dependency: "direct main"
description:
name: sqflite
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.2"
version: "2.0.3"
sqflite_common:
dependency: transitive
description:
name: sqflite_common
url: "https://pub.dartlang.org"
source: hosted
version: "2.2.0"
version: "2.2.1+1"
stack_trace:
dependency: transitive
description:
@ -825,14 +853,14 @@ packages:
name: synchronized
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.0"
version: "3.0.0+2"
table_calendar:
dependency: "direct main"
description:
name: table_calendar
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.4"
version: "3.0.6"
term_glyph:
dependency: transitive
description:
@ -846,70 +874,70 @@ packages:
name: test_api
url: "https://pub.dartlang.org"
source: hosted
version: "0.4.8"
version: "0.4.9"
typed_data:
dependency: transitive
description:
name: typed_data
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.0"
version: "1.3.1"
url_launcher:
dependency: "direct main"
description:
name: url_launcher
url: "https://pub.dartlang.org"
source: hosted
version: "6.0.20"
version: "6.1.5"
url_launcher_android:
dependency: transitive
description:
name: url_launcher_android
url: "https://pub.dartlang.org"
source: hosted
version: "6.0.15"
version: "6.0.17"
url_launcher_ios:
dependency: transitive
description:
name: url_launcher_ios
url: "https://pub.dartlang.org"
source: hosted
version: "6.0.15"
version: "6.0.17"
url_launcher_linux:
dependency: transitive
description:
name: url_launcher_linux
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.0"
version: "3.0.1"
url_launcher_macos:
dependency: transitive
description:
name: url_launcher_macos
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.0"
version: "3.0.1"
url_launcher_platform_interface:
dependency: transitive
description:
name: url_launcher_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.5"
version: "2.1.0"
url_launcher_web:
dependency: transitive
description:
name: url_launcher_web
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.9"
version: "2.0.12"
url_launcher_windows:
dependency: transitive
description:
name: url_launcher_windows
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.0"
version: "3.0.1"
uuid:
dependency: transitive
description:
@ -923,70 +951,63 @@ packages:
name: vector_math
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.1"
version: "2.1.2"
version:
dependency: "direct main"
description:
name: version
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.0"
very_good_analysis:
dependency: transitive
description:
name: very_good_analysis
url: "https://pub.dartlang.org"
source: hosted
version: "2.4.0"
version: "3.0.2"
video_player:
dependency: "direct main"
description:
name: video_player
url: "https://pub.dartlang.org"
source: hosted
version: "2.3.0"
version: "2.4.5"
video_player_android:
dependency: transitive
description:
name: video_player_android
url: "https://pub.dartlang.org"
source: hosted
version: "2.3.0"
version: "2.3.8"
video_player_avfoundation:
dependency: transitive
description:
name: video_player_avfoundation
url: "https://pub.dartlang.org"
source: hosted
version: "2.3.0"
version: "2.3.5"
video_player_platform_interface:
dependency: transitive
description:
name: video_player_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "5.1.0"
version: "5.1.3"
video_player_web:
dependency: transitive
description:
name: video_player_web
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.7"
version: "2.0.12"
video_thumbnail:
dependency: "direct main"
description:
name: video_thumbnail
url: "https://pub.dartlang.org"
source: hosted
version: "0.4.6"
version: "0.5.2"
wakelock:
dependency: "direct main"
description:
name: wakelock
url: "https://pub.dartlang.org"
source: hosted
version: "0.6.1+2"
version: "0.6.2"
wakelock_macos:
dependency: transitive
description:
@ -1021,21 +1042,21 @@ packages:
name: web_socket_channel
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
version: "2.2.0"
webrtc_interface:
dependency: transitive
description:
name: webrtc_interface
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.2"
version: "1.0.5"
win32:
dependency: transitive
description:
name: win32
url: "https://pub.dartlang.org"
source: hosted
version: "2.4.1"
version: "2.7.0"
xdg_directories:
dependency: transitive
description:
@ -1049,14 +1070,14 @@ packages:
name: xml
url: "https://pub.dartlang.org"
source: hosted
version: "5.3.1"
version: "6.1.0"
yaml:
dependency: transitive
description:
name: yaml
url: "https://pub.dartlang.org"
source: hosted
version: "3.1.0"
version: "3.1.1"
sdks:
dart: ">=2.16.0 <3.0.0"
flutter: ">=2.10.0"
dart: ">=2.17.0 <3.0.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.
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 1.1.11+18
version: 1.2.2+22
environment:
sdk: '>=2.12.0 <3.0.0'
@ -68,7 +68,7 @@ dependencies:
random_string: ^2.3.1
# Display zoomable images
photo_view: ^0.13.0
photo_view: ^0.14.0
# Check Internet connection
connectivity_plus: ^2.2.1
@ -80,19 +80,19 @@ dependencies:
event_bus: ^2.0.0
# WebRTC calls
flutter_webrtc: ^0.8.3
flutter_webrtc: ^0.9.1
# Prevent phone from auto-locking during calls
wakelock: ^0.6.1+2
# Pick any kind of file
file_picker: ^4.5.0
file_picker: ^5.0.1
# Get information about current version
package_info_plus: ^1.4.0
# Version manager
version: ^2.0.0
version: ^3.0.2
# Get path to temporary files
path_provider: ^2.0.9
@ -104,7 +104,7 @@ dependencies:
clipboard: ^0.1.3
# Video / Audio player
video_player: ^2.3.0
video_player: ^2.4.3
chewie_audio: ^1.3.0
chewie: ^1.3.0
@ -112,7 +112,7 @@ dependencies:
mime: ^1.0.1
# Create video thumbnails
video_thumbnail: ^0.4.6
video_thumbnail: ^0.5.0
# Record audio file
record_mp3: ^3.0.0
@ -127,11 +127,11 @@ dependencies:
flutter_colorpicker: ^1.0.3
# Image cropper
image_cropper: ^1.5.0
image_cropper: ^2.0.3
# Firebase cloud messaging (for push notifications)
firebase_core: ^1.13.1
firebase_messaging: ^11.2.8
firebase_messaging: ^12.0.1
# Forez presence
table_calendar: ^3.0.4