1
0
mirror of https://gitlab.com/comunic/comunicmobile synced 2025-07-01 06:03:29 +00:00

51 Commits

Author SHA1 Message Date
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
2ee4590364 Improve management of cache key 2022-03-12 10:57:11 +01:00
2a50190e94 Add cache key for conversation messages 2022-03-12 10:31:13 +01:00
5040fbb101 Fix refresh issue 2022-03-12 10:14:32 +01:00
11829273e2 Revert "Fix refresh issue"
This reverts commit 56c5eb33
2022-03-12 10:13:44 +01:00
56c5eb335b Fix refresh issue 2022-03-12 10:13:32 +01:00
0bd7426813 Attempt to keep post create form 2022-03-12 09:58:23 +01:00
75c596226b Show a message if user has no conversation yet 2022-03-12 09:47:58 +01:00
a0abdc4f1b Show a message if user has no friend yet 2022-03-12 09:44:21 +01:00
2ac5caaf96 Fix a bug on friends list screen 2022-03-12 09:42:07 +01:00
98354ee3cc Update compile target SDK version 2022-03-11 17:29:32 +01:00
9e6d3761fe Finish to fix notices 2022-03-11 17:16:46 +01:00
48c9ee37b6 Fix a few notices 2022-03-11 17:13:54 +01:00
41446f0e5b Fix a few notices 2022-03-11 17:09:37 +01:00
0f9a59b4a7 Fix a few notices 2022-03-11 17:02:06 +01:00
f0f7096c94 Fix a few notices 2022-03-11 16:41:29 +01:00
adbc036c16 Fix a few notices 2022-03-11 16:40:56 +01:00
45e8f34c81 Fix a few notices 2022-03-11 16:36:42 +01:00
06312512a6 Fix a few notices 2022-03-11 16:27:01 +01:00
5398970868 Fix all warnings 2022-03-11 16:21:35 +01:00
820491b09a Continue to fix issues 2022-03-10 20:39:06 +01:00
7a0b44e446 Continue to fix issues 2022-03-10 20:36:55 +01:00
299a95ea45 Continue to fix issues 2022-03-10 20:28:07 +01:00
3a997cdc56 Start to fix null safety migration errors 2022-03-10 19:39:57 +01:00
ab2c5da0da Progressing with null safety migration 2022-03-10 19:02:06 +01:00
2424fd38d6 Prepare null safety migration 2022-03-10 18:47:16 +01:00
3c08384a4f Migrate from package_info to package_info_plus 2022-03-10 18:42:48 +01:00
c199540aff Migrate from connectivity to connectivity_plus 2022-03-10 18:37:59 +01:00
49a9186978 Update emoji package 2022-03-10 18:31:29 +01:00
1f3230363e Update permission_handler dependency 2022-03-10 18:21:37 +01:00
1a4988d192 Update chewie & wakelock dependencies 2022-03-10 18:14:59 +01:00
cd7cafe315 Run pub update 2022-03-10 18:07:15 +01:00
42b4f99efa Update Flutter to v2.10 2022-03-10 17:28:21 +01:00
173487f801 Start to work on version 1.1.11 2021-12-30 14:34:19 +01:00
270 changed files with 4066 additions and 3948 deletions

View File

@ -22,6 +22,7 @@ if (flutterVersionName == null) {
}
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
@ -33,16 +34,22 @@ if (keystorePropertiesFile.exists()) {
}
android {
compileSdkVersion 31
compileSdkVersion flutter.compileSdkVersion
compileOptions {
// Required to use WebRTC
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
sourceSets {
main.java.srcDirs += 'src/main/java'
}
lintOptions {
disable 'InvalidPackage'
@ -52,8 +59,8 @@ android {
defaultConfig {
applicationId "org.communiquons.comunic"
minSdkVersion 21
targetSdkVersion 31
minSdkVersion 23
targetSdkVersion flutter.targetSdkVersion
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
@ -79,9 +86,7 @@ android {
buildTypes {
release {
useProguard true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
@ -112,6 +117,7 @@ flutter {
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'com.neovisionaries:nv-websocket-client:2.14'
testImplementation 'junit:junit:4.13.1'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'

View File

@ -1,6 +0,0 @@
package org.communiquons.comunic
import io.flutter.embedding.android.FlutterActivity
class MainActivity: FlutterActivity() {
}

View File

@ -1,4 +1,5 @@
buildscript {
ext.kotlin_version = '1.5.30'
repositories {
google()
mavenCentral()
@ -6,6 +7,7 @@ buildscript {
dependencies {
classpath 'com.android.tools.build:gradle:4.1.3'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// Firebase
classpath 'com.google.gms:google-services:4.3.10'

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

@ -8,7 +8,7 @@ import 'package:comunic/models/advanced_group_info.dart';
///
/// @author Pierre Hubert
AdvancedGroupInfo _forezGroup;
AdvancedGroupInfo? _forezGroup;
class ForezGroupHelper {
static Future<void> setId(int groupID) async {
@ -16,7 +16,7 @@ class ForezGroupHelper {
.setInt(PreferencesKeyList.FOREZ_GROUP, groupID);
}
static Future<int> getId() async {
static Future<int?> getId() async {
return (await PreferencesHelper.getInstance())
.getInt(PreferencesKeyList.FOREZ_GROUP);
}
@ -27,7 +27,7 @@ class ForezGroupHelper {
_forezGroup = res.info;
}
static AdvancedGroupInfo getGroup() => _forezGroup;
static AdvancedGroupInfo? getGroup() => _forezGroup;
}
AdvancedGroupInfo get forezGroup => ForezGroupHelper.getGroup();
AdvancedGroupInfo? get forezGroup => ForezGroupHelper.getGroup();

View File

@ -11,10 +11,10 @@ import 'package:flutter/material.dart';
class ForezConfig extends Config {
ForezConfig({
@required String apiServerName,
@required String apiServerUri,
@required bool apiServerSecure,
@required String clientName,
required String apiServerName,
required String apiServerUri,
required bool apiServerSecure,
required String clientName,
}) : super(
apiServerName: apiServerName,
apiServerUri: apiServerUri,

View File

@ -11,7 +11,7 @@ import 'package:comunic/models/config.dart';
/// Fix HTTPS issue
class MyHttpOverride extends HttpOverrides {
@override
HttpClient createHttpClient(SecurityContext context) {
HttpClient createHttpClient(SecurityContext? context) {
return super.createHttpClient(context)
..badCertificateCallback = (cert, host, port) {
return host == "devweb.local"; // Forcefully trust local website

View File

@ -29,7 +29,7 @@ List<Widget> buildTour(TourRouteState state) {
msgTwo: tr("Let's first join a Forez group!"),
),
JoinForezGroupPane(
key: state.pubKeys[_JOIN_GROUP_KEY_ID],
key: state.pubKeys[_JOIN_GROUP_KEY_ID] as GlobalKey<JoinGroupPaneBodyState>?,
onUpdated: () => state.rebuild(),
),
FirstTourPane(
@ -51,7 +51,7 @@ List<Widget> buildTour(TourRouteState state) {
// Forez specific features
PresentationPane(
icon: Icons.calendar_today,
title: tr("Presence in Forez"),
title: tr("Presence in Forez")!,
text: tr(
"Easily specify the days you are in Forez plain, so that everyone can know it!"),
actionTitle: tr("Do it now!"),
@ -62,7 +62,7 @@ List<Widget> buildTour(TourRouteState state) {
// Chat pane
PresentationPane(
icon: Icons.question_answer,
title: tr("Conversations"),
title: tr("Conversations")!,
text: tr(
"#Forez now integrates the conversation system of Comunic, so you have access both to public and private conversations!"),
),

View File

@ -16,17 +16,17 @@ import 'package:flutter/material.dart';
class JoinForezGroupPane extends PresentationPane {
JoinForezGroupPane({
@required Function() onUpdated,
@required GlobalKey<JoinGroupPaneBodyState> key,
required Function() onUpdated,
required GlobalKey<JoinGroupPaneBodyState>? key,
}) : super(
icon: Icons.login,
title: tr("Join a Forez group"),
title: tr("Join a Forez group")!,
child: (c) => _JoinGroupPaneBody(
key: key,
onUpdated: onUpdated,
),
canGoNext: key?.currentState?.canGoNext ?? false,
onTapNext: (c) => key.currentState.validateChoice(),
onTapNext: (c) => key!.currentState!.validateChoice(),
);
}
@ -34,10 +34,9 @@ class _JoinGroupPaneBody extends StatefulWidget {
final Function() onUpdated;
const _JoinGroupPaneBody({
Key key,
@required this.onUpdated,
}) : assert(onUpdated != null),
super(key: key);
Key? key,
required this.onUpdated,
}) : super(key: key);
@override
JoinGroupPaneBodyState createState() => JoinGroupPaneBodyState();
@ -46,10 +45,10 @@ class _JoinGroupPaneBody extends StatefulWidget {
class JoinGroupPaneBodyState extends State<_JoinGroupPaneBody> {
final _key = GlobalKey<AsyncScreenWidgetState>();
List<Group> _groups;
int _currChoice;
late List<Group> _groups;
int? _currChoice;
bool get canGoNext => _currChoice != null && _currChoice > 0;
bool get canGoNext => _currChoice != null && _currChoice! > 0;
Group get _currGroup => _groups.firstWhere((e) => e.id == _currChoice);
@ -65,17 +64,17 @@ class JoinGroupPaneBodyState extends State<_JoinGroupPaneBody> {
key: _key,
onReload: _load,
onBuild: onBuild,
errorMessage: tr("Failed to load the list of Forez groups!"));
errorMessage: tr("Failed to load the list of Forez groups!")!);
Widget onBuild() => ConstrainedBox(
constraints: BoxConstraints(maxWidth: 300),
child: Column(
children: [
Text(tr("Please choose now the Forez group you want to join...")),
Text(tr("Please choose now the Forez group you want to join...")!),
]..addAll(_groups.map((e) => RadioListTile(
value: e.id,
groupValue: _currChoice,
onChanged: (v) => setState(() => _currChoice = e.id),
onChanged: (dynamic v) => setState(() => _currChoice = e.id),
title: Text(e.name),
subtitle: Text(e.membershipText),
))),
@ -112,7 +111,7 @@ class JoinGroupPaneBodyState extends State<_JoinGroupPaneBody> {
await alert(context,
"${tr("You can not access this group yet, please wait for a member of the group to accept your request.")}\n${tr("Hopefully this will not be too long.")}\n${tr("Please check back soon!")}");
_key.currentState.refresh();
_key.currentState!.refresh();
return false;
}
@ -121,8 +120,8 @@ class JoinGroupPaneBodyState extends State<_JoinGroupPaneBody> {
return true;
} catch (e, s) {
logError(e, s);
snack(context, tr("Failed to register to group!"));
_key.currentState.refresh();
snack(context, tr("Failed to register to group!")!);
_key.currentState!.refresh();
return false;
}
}

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
///
@ -24,10 +24,9 @@ class ForezMemberProfileRoute extends StatefulWidget {
final int userID;
const ForezMemberProfileRoute({
Key key,
@required this.userID,
}) : assert(userID != null),
super(key: key);
Key? key,
required this.userID,
}) : super(key: key);
@override
_ForezMemberProfileRouteState createState() =>
@ -39,13 +38,14 @@ class _ForezMemberProfileRouteState extends State<ForezMemberProfileRoute> {
final _key = GlobalKey<AsyncScreenWidgetState>();
AdvancedUserInfo _user;
PresenceSet _presence;
late AdvancedUserInfo _user;
late PresenceSet _presence;
Future<void> _load() async {
_user = await ForezGroupsHelper.getMemberInfo(forezGroup.id, widget.userID);
_user =
await ForezGroupsHelper.getMemberInfo(forezGroup!.id, widget.userID);
_presence =
await ForezPresenceHelper.getForUser(forezGroup.id, widget.userID);
await ForezPresenceHelper.getForUser(forezGroup!.id, widget.userID);
}
@override
@ -56,25 +56,25 @@ class _ForezMemberProfileRouteState extends State<ForezMemberProfileRoute> {
loadingWidget: _buildLoading(),
errorWidget: _buildError(),
errorMessage: tr(
"Failed to load user information, maybe it is not a Forez member yet?"));
"Failed to load user information, maybe it is not a Forez member yet?")!);
Widget _buildLoading() => Scaffold(
appBar: AppBar(
title: Text(tr("Loading...")),
title: Text(tr("Loading...")!),
),
body: buildCenteredProgressBar(),
);
Widget _buildError() => Scaffold(
appBar: AppBar(
title: Text(tr("Error")),
title: Text(tr("Error")!),
),
body: buildErrorCard(
tr("Failed to load user information, maybe it is not a Forez member yet?"),
actions: [
MaterialButton(
onPressed: () => _key.currentState.refresh(),
child: Text(tr("Try again")),
onPressed: () => _key.currentState!.refresh(),
child: Text(tr("Try again")!),
textColor: Colors.white,
)
]),
@ -97,9 +97,7 @@ class _ForezMemberProfileRouteState extends State<ForezMemberProfileRoute> {
background: Stack(
fit: StackFit.expand,
children: <Widget>[
_user.accountImageURL == null
? Container()
: CachedNetworkImage(
CachedNetworkImage(
fit: BoxFit.cover,
imageUrl: _user.accountImageURL,
height: _appBarHeight,
@ -135,7 +133,7 @@ class _ForezMemberProfileRouteState extends State<ForezMemberProfileRoute> {
: ListTile(
leading: Icon(Icons.note),
title: TextWidget(content: DisplayedString(_user.publicNote)),
subtitle: Text(tr("Note")),
subtitle: Text(tr("Note")!),
),
// Email address
@ -143,9 +141,9 @@ class _ForezMemberProfileRouteState extends State<ForezMemberProfileRoute> {
? Container()
: ListTile(
leading: Icon(Icons.email),
title: Text(_user.emailAddress),
subtitle: Text(tr("Email address")),
trailing: CopyIcon(_user.emailAddress),
title: Text(_user.emailAddress!),
subtitle: Text(tr("Email address")!),
trailing: CopyIcon(_user.emailAddress!),
),
// Location
@ -153,9 +151,9 @@ class _ForezMemberProfileRouteState extends State<ForezMemberProfileRoute> {
? Container()
: ListTile(
leading: Icon(Icons.location_on),
title: Text(_user.location),
subtitle: Text(tr("Location")),
trailing: CopyIcon(_user.location),
title: Text(_user.location!),
subtitle: Text(tr("Location")!),
trailing: CopyIcon(_user.location!),
),
// Website
@ -164,20 +162,20 @@ class _ForezMemberProfileRouteState extends State<ForezMemberProfileRoute> {
: ListTile(
leading: Icon(Icons.link),
title: Text(_user.personalWebsite),
subtitle: Text(tr("Website")),
subtitle: Text(tr("Website")!),
trailing: IconButton(
icon: Icon(Icons.open_in_new),
onPressed: () => launch(_user.personalWebsite),
onPressed: () => launchUrlString(_user.personalWebsite),
),
),
Divider(),
ListTile(
leading: Icon(Icons.calendar_today),
title: Text(tr("Presence in Forez")),
title: Text(tr("Presence in Forez")!),
subtitle: Text(_presence.containsDate(DateTime.now())
? tr("Present today")
: tr("Absent")),
? tr("Present today")!
: tr("Absent")!),
),
PresenceCalendarWidget(presenceSet: _presence),
Divider(),

View File

@ -21,7 +21,7 @@ import 'package:flutter/material.dart';
/// @author Pierre Hubert
class ForezRoute extends StatefulWidget implements MainRoute {
const ForezRoute({Key key}) : super(key: key);
const ForezRoute({Key? key}) : super(key: key);
@override
State<StatefulWidget> createState() => _MainRouteState();
@ -40,7 +40,7 @@ class _MainRouteState extends MainController {
@override
Widget build(BuildContext context) {
if (forezGroup == null) return Text(tr("Missing Forez group!"));
if (forezGroup == null) return Text(tr("Missing Forez group!")!);
return StatusWidget(
child: (c) => SafeArea(
@ -67,11 +67,11 @@ class _MainRouteState extends MainController {
@override
void openConversation(Conversation conv, {fullScreen: false}) {
// Forcefully open conversations in a "normal" way (do not display groups)
openConversationById(conv.id, fullScreen: fullScreen);
openConversationById(conv.id!, fullScreen: fullScreen);
}
@override
void openGroup(int groupID, {int conversationID}) => _unsupportedFeature();
void openGroup(int groupID, {int? conversationID}) => _unsupportedFeature();
@override
void openUserPage(int userID) => pushPage(PageInfo(
@ -92,7 +92,7 @@ class _MainRouteState extends MainController {
enum _PopupMenuItems { ACTION_SETTINGS, ACTION_SIGN_OUT }
class ForezRouteBody extends StatefulWidget {
ForezRouteBody({Key key}) : super(key: key);
ForezRouteBody({Key? key}) : super(key: key);
@override
_ForezRouteBodyState createState() => _ForezRouteBodyState();
@ -112,7 +112,7 @@ class _ForezRouteBodyState extends SafeState<ForezRouteBody> {
length: _tabs.length,
child: Scaffold(
appBar: AppBar(
title: Text(forezGroup.name),
title: Text(forezGroup!.name),
actions: <Widget>[_buildPopupMenuButton()],
bottom: TabBar(tabs: _tabs),
),
@ -129,11 +129,11 @@ class _ForezRouteBodyState extends SafeState<ForezRouteBody> {
Widget _buildPopupMenuButton() => PopupMenuButton<_PopupMenuItems>(
itemBuilder: (c) => [
PopupMenuItem(
child: Text(tr("Settings")),
child: Text(tr("Settings")!),
value: _PopupMenuItems.ACTION_SETTINGS,
),
PopupMenuItem(
child: Text(tr("Sign out")),
child: Text(tr("Sign out")!),
value: _PopupMenuItems.ACTION_SIGN_OUT,
),
],
@ -143,10 +143,10 @@ class _ForezRouteBodyState extends SafeState<ForezRouteBody> {
void _onMenuSelection(_PopupMenuItems value) {
switch (value) {
case _PopupMenuItems.ACTION_SETTINGS:
MainController.of(context).openSettings();
MainController.of(context)!.openSettings();
break;
case _PopupMenuItems.ACTION_SIGN_OUT:
MainController.of(context).requestLogout();
MainController.of(context)!.requestLogout();
break;
}
}
@ -155,29 +155,29 @@ class _ForezRouteBodyState extends SafeState<ForezRouteBody> {
// Posts tab
_Tab(
icon: Icons.auto_stories,
title: tr("Posts"),
widget: () => GroupPostsSection(group: forezGroup),
title: tr("Posts")!,
widget: () => GroupPostsSection(group: forezGroup!),
),
// Presence tab
_Tab(
icon: Icons.calendar_today,
title: tr("Presence"),
widget: () => ForezPresenceSection(groupID: forezGroup.id),
title: tr("Presence")!,
widget: () => ForezPresenceSection(groupID: forezGroup!.id),
),
// Conversations tab
_Tab(
icon: Icons.question_answer,
title: tr("Conversations"),
title: tr("Conversations")!,
widget: () => ConversationsListScreen(),
isUnread: (c) => StatusWidgetState.of(c).unreadConversations > 0,
isUnread: (c) => StatusWidgetState.of(c)!.unreadConversations! > 0,
),
// Directory tab
_Tab(
icon: Icons.import_contacts,
title: tr("Directory"),
title: tr("Directory")!,
widget: () => ForezDirectoryScreen(),
),
];
@ -203,14 +203,12 @@ class _Tab {
final IconData icon;
final String title;
final Widget Function() widget;
final bool Function(BuildContext) isUnread;
final bool Function(BuildContext)? isUnread;
const _Tab({
@required this.icon,
@required this.title,
@required this.widget,
required this.icon,
required this.title,
required this.widget,
this.isUnread,
}) : assert(icon != null),
assert(title != null),
assert(widget != null);
});
}

View File

@ -7,18 +7,18 @@ import 'package:flutter/material.dart';
///
/// @author Pierre Hubert
Future<User> searchUser(BuildContext context, UsersList users) async {
return await showSearch<User>(
Future<User?> searchUser(BuildContext context, UsersList users) async {
return await showSearch<User?>(
context: context, delegate: _SearchDelegate(users));
}
class _SearchDelegate extends SearchDelegate<User> {
class _SearchDelegate extends SearchDelegate<User?> {
final UsersList _usersList;
_SearchDelegate(this._usersList) : assert(_usersList != null);
_SearchDelegate(this._usersList);
@override
List<Widget> buildActions(BuildContext context) => null;
List<Widget>? buildActions(BuildContext context) => null;
@override
Widget buildLeading(BuildContext context) => IconButton(
@ -28,7 +28,7 @@ class _SearchDelegate extends SearchDelegate<User> {
@override
Widget buildSuggestions(BuildContext context) {
final list = _usersList
final List<User> list = _usersList
.where((element) =>
element.fullName.toLowerCase().contains(query.toLowerCase()))
.toList();

View File

@ -35,11 +35,11 @@ class ForezDirectoryScreen extends StatefulWidget {
class _ForezDirectoryScreenState extends State<ForezDirectoryScreen> {
final _key = GlobalKey<AsyncScreenWidgetState>();
UsersList _users;
GroupMembersList _members;
late UsersList _users;
late GroupMembersList _members;
Future<void> _load() async {
_members = await GroupsHelper.getMembersList(forezGroup.id);
_members = await GroupsHelper.getMembersList(forezGroup!.id);
_users = await UsersHelper().getListWithThrow(_members.usersID);
}
@ -49,7 +49,7 @@ class _ForezDirectoryScreenState extends State<ForezDirectoryScreen> {
AsyncScreenWidget(
onReload: _load,
onBuild: onBuild,
errorMessage: tr("Failed to load members list!"),
errorMessage: tr("Failed to load members list!")!,
key: _key,
),
Positioned(
@ -88,13 +88,13 @@ class _ForezDirectoryScreenState extends State<ForezDirectoryScreen> {
"Do you really want to cancel the invitation sent to %u%?",
args: {"u": user.fullName}))) return;
await GroupsHelper.cancelInvitation(forezGroup.id, user.id);
_key.currentState.refresh();
await GroupsHelper.cancelInvitation(forezGroup!.id, user.id);
_key.currentState!.refresh();
break;
case _PopupMenuActions.ACCEPT_REQUEST:
await GroupsHelper.respondRequest(forezGroup.id, user.id, true);
_key.currentState.refresh();
await GroupsHelper.respondRequest(forezGroup!.id, user.id, true);
_key.currentState!.refresh();
break;
case _PopupMenuActions.REJECT_REQUEST:
@ -104,13 +104,13 @@ class _ForezDirectoryScreenState extends State<ForezDirectoryScreen> {
"Do you really want to reject the request of %u% to join the Forez group?",
args: {"u": user.fullName}))) return;
await GroupsHelper.respondRequest(forezGroup.id, user.id, false);
_key.currentState.refresh();
await GroupsHelper.respondRequest(forezGroup!.id, user.id, false);
_key.currentState!.refresh();
break;
}
} catch (e, s) {
logError(e, s);
snack(context, tr("Error while processing action!"));
snack(context, tr("Error while processing action!")!);
}
}
@ -125,7 +125,7 @@ class _ForezDirectoryScreenState extends State<ForezDirectoryScreen> {
}
void _openUserProfile(User user) =>
MainController.of(context).openUserPage(user.id);
MainController.of(context)!.openUserPage(user.id);
}
class _ForezMemberTile extends StatelessWidget {
@ -135,11 +135,11 @@ class _ForezMemberTile extends StatelessWidget {
final Function(User) onTap;
const _ForezMemberTile({
Key key,
@required this.user,
@required this.member,
@required this.selectedAction,
@required this.onTap,
Key? key,
required this.user,
required this.member,
required this.selectedAction,
required this.onTap,
}) : super(key: key);
@override
@ -147,7 +147,7 @@ class _ForezMemberTile extends StatelessWidget {
leading: AccountImageWidget(user: user),
title: Text(user.fullName),
subtitle: Text(member.membershipText),
trailing: !member.isAtLeastMember && forezGroup.isAtLeastModerator
trailing: !member.isAtLeastMember && forezGroup!.isAtLeastModerator
? (member.isInvited
? _buildInvitedButton()
: _buildRequestedButton())
@ -155,7 +155,7 @@ class _ForezMemberTile extends StatelessWidget {
onTap: member.isAtLeastMember ? () => onTap(user) : null,
);
Widget _buildConversationButton() => user.id == userID()
Widget? _buildConversationButton() => user.id == userID()
? null
: IconButton(
icon: Icon(Icons.message),
@ -201,18 +201,13 @@ class _MembershipButton extends StatelessWidget {
final IconData icon;
const _MembershipButton({
Key key,
@required this.user,
@required this.action,
@required this.onTap,
@required this.color,
@required this.icon,
}) : assert(user != null),
assert(action != null),
assert(onTap != null),
assert(color != null),
assert(icon != null),
super(key: key);
Key? key,
required this.user,
required this.action,
required this.onTap,
required this.color,
required this.icon,
}) : super(key: key);
@override
Widget build(BuildContext context) => ElevatedButton(

View File

@ -4,12 +4,15 @@
// ignore_for_file: directives_ordering
// ignore_for_file: lines_longer_than_80_chars
// ignore_for_file: depend_on_referenced_packages
import 'package:connectivity_for_web/connectivity_for_web.dart';
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';
import 'package:url_launcher_web/url_launcher_web.dart';
import 'package:video_player_web/video_player_web.dart';
@ -19,11 +22,13 @@ import 'package:flutter_web_plugins/flutter_web_plugins.dart';
// ignore: public_member_api_docs
void registerPlugins(Registrar registrar) {
ConnectivityPlugin.registerWith(registrar);
ConnectivityPlusPlugin.registerWith(registrar);
FilePickerWeb.registerWith(registrar);
FirebaseCoreWeb.registerWith(registrar);
FirebaseMessagingWeb.registerWith(registrar);
ImageCropperPlugin.registerWith(registrar);
ImagePickerPlugin.registerWith(registrar);
PackageInfoPlugin.registerWith(registrar);
SharedPreferencesPlugin.registerWith(registrar);
UrlLauncherPlugin.registerWith(registrar);
VideoPlayerPlugin.registerWith(registrar);

View File

@ -26,7 +26,7 @@ enum CreateAccountResult {
class AccountHelper {
// Current user ID
static int _currentUserID = -1;
static int? _currentUserID = -1;
/// Checkout whether current user is signed in or not
///
@ -67,7 +67,7 @@ class AccountHelper {
}
// Save current user ID
final preferences = await PreferencesHelper.getInstance();
final preferences = await (PreferencesHelper.getInstance());
await preferences.setInt(PreferencesKeyList.USER_ID, userID);
_currentUserID = userID;
@ -130,14 +130,14 @@ class AccountHelper {
.getObject()["exists"];
/// Get current user email address
static Future<String> getCurrentAccountEmailAddress() async =>
static Future<String?> getCurrentAccountEmailAddress() async =>
(await APIRequest.withLogin("account/mail")
.execWithThrowGetObject())["mail"];
/// Check out whether security questions have been set for an account or not
///
/// Throws in case of failure
static Future<bool> hasSecurityQuestions(String email) async =>
static Future<bool?> hasSecurityQuestions(String email) async =>
(await APIRequest.withoutLogin("account/has_security_questions")
.addString("email", email)
.execWithThrow())
@ -146,7 +146,7 @@ class AccountHelper {
/// Get the security questions of the user
///
/// Throws in case of failure
static Future<List<String>> getSecurityQuestions(String email) async =>
static Future<List<String>?> getSecurityQuestions(String? email) async =>
((await APIRequest.withoutLogin("account/get_security_questions")
.addString("email", email)
.execWithThrow())
@ -158,8 +158,8 @@ class AccountHelper {
/// Throws an [Exception] in case of failure
///
/// Returns a password reset token in case of success
static Future<String> checkAnswers(
String email, List<String> answers) async =>
static Future<String?> checkAnswers(
String? email, List<String> answers) async =>
(await APIRequest.withoutLogin("account/check_security_answers")
.addString("email", email)
.addString("answers",
@ -195,7 +195,7 @@ class AccountHelper {
.execWithThrow();
/// Get current user ID from the server
Future<int> _downloadCurrentUserID() async {
Future<int?> _downloadCurrentUserID() async {
final response = await APIRequest.withLogin("account/id").exec();
if (response.code != 200) return null;
@ -210,10 +210,10 @@ class AccountHelper {
}
/// Check if current user ID is loaded or not
static bool get isUserIDLoaded => _currentUserID > 0;
static bool get isUserIDLoaded => _currentUserID! > 0;
/// Get the ID of the currently signed in user
static int getCurrentUserID() {
static int? getCurrentUserID() {
if (_currentUserID == -1) throw "Current user ID has not been loaded yet!";
return _currentUserID;
}

View File

@ -41,13 +41,13 @@ class APIHelper {
else
url = Uri.https(config().apiServerName, path);
final data = FormData.fromMap(request.args);
final data = FormData.fromMap(request.args!);
// Process files (if required)
if (multipart) {
// Process filesystem files
for (final key in request.files.keys) {
var v = request.files[key];
var v = request.files[key]!;
data.files.add(MapEntry(
key,
await MultipartFile.fromFile(v.path,
@ -56,11 +56,11 @@ class APIHelper {
// Process in-memory files
for (final key in request.bytesFiles.keys) {
var v = request.bytesFiles[key];
var v = request.bytesFiles[key]!;
data.files.add(MapEntry(
key,
MultipartFile.fromBytes(
v.bytes,
v.bytes!,
filename: v.filename.split("/").last,
contentType: v.type,
)));
@ -85,9 +85,9 @@ class APIHelper {
EventsHelper.emit(InvalidLoginTokensEvent());
if (response.statusCode != HttpStatus.ok)
return APIResponse(response.statusCode, response.data);
return APIResponse(response.statusCode!, response.data);
return APIResponse(response.statusCode, response.data);
return APIResponse(response.statusCode!, response.data);
} catch (e, stack) {
print(e.toString());
print("Could not execute a request!");

View File

@ -39,12 +39,12 @@ class CallsHelper {
.cast<CallMember>());
/// Request an offer to access another peer's stream
static Future<void> requestOffer(int callID, int peerID) async =>
static Future<void> requestOffer(int callID, int? peerID) async =>
await ws("calls/request_offer", {"callID": callID, "peerID": peerID});
/// Send a Session Description message to the server
static Future<void> sendSessionDescription(
int callID, int peerID, RTCSessionDescription sdp) async =>
int callID, int? peerID, RTCSessionDescription sdp) async =>
await ws("calls/signal", {
"callID": callID,
"peerID": peerID,
@ -54,7 +54,7 @@ class CallsHelper {
/// Send an IceCandidate
static Future<void> sendIceCandidate(
int callID, int peerID, RTCIceCandidate candidate) async =>
int callID, int? peerID, RTCIceCandidate candidate) async =>
await ws("calls/signal", {
"callID": callID,
"peerID": peerID,

View File

@ -27,7 +27,7 @@ class CommentsHelper {
}
/// Get a single comment from the server, specified by its [id]
Future<Comment> getSingle(int id) async {
Future<Comment?> getSingle(int id) async {
final response = await APIRequest(
uri: "comments/get_single",
needLogin: true,
@ -39,7 +39,7 @@ class CommentsHelper {
}
/// Update comment content
Future<bool> updateContent(int id, String newContent) async {
Future<bool> updateContent(int id, String? newContent) async {
return (await APIRequest(uri: "comments/edit", needLogin: true, args: {
"commentID": id.toString(),
"content": newContent,

View File

@ -21,7 +21,6 @@ import 'package:comunic/utils/account_utils.dart';
import 'package:comunic/utils/color_utils.dart';
import 'package:comunic/utils/dart_color.dart';
import 'package:dio/dio.dart';
import 'package:meta/meta.dart';
/// Conversation helper
///
@ -39,7 +38,7 @@ class ConversationsHelper {
/// Throws in case of failure
static Future<int> createConversation(NewConversation settings) async {
final response = await APIRequest.withLogin("conversations/create", args: {
"name": settings.name ?? "",
"name": settings.name,
"follow": settings.follow ? "true" : "false",
"users": settings.members.join(","),
"color": colorToHex(settings.color)
@ -53,7 +52,7 @@ class ConversationsHelper {
/// Add a member to a conversation.
///
/// Throws in case of failure
static Future<void> addMember(int convID, int userID) async =>
static Future<void> addMember(int? convID, int? userID) async =>
await APIRequest.withLogin("conversations/addMember")
.addInt("convID", convID)
.addInt("userID", userID)
@ -62,7 +61,7 @@ class ConversationsHelper {
/// Remove a member from a conversation.
///
/// Throws in case of failure
static Future<void> removeMember(int convID, int userID) async =>
static Future<void> removeMember(int? convID, int? userID) async =>
await APIRequest.withLogin("conversations/removeMember")
.addInt("convID", convID)
.addInt("userID", userID)
@ -90,8 +89,8 @@ class ConversationsHelper {
// Update conversation settings
if (settings.isComplete)
request
.addString("name", settings.name ?? "")
.addBool("canEveryoneAddMembers", settings.canEveryoneAddMembers)
.addString("name", settings.name)
.addBool("canEveryoneAddMembers", settings.canEveryoneAddMembers!)
.addString("color", colorToHex(settings.color));
await request.execWithThrow();
@ -104,7 +103,7 @@ class ConversationsHelper {
/// Set a new conversation logo
///
/// Throws in case of failure
static Future<void> changeImage(int convID, BytesFile file) async =>
static Future<void> changeImage(int? convID, BytesFile file) async =>
await APIRequest.withLogin("conversations/change_image")
.addInt("convID", convID)
.addBytesFile("file", file)
@ -113,13 +112,13 @@ class ConversationsHelper {
/// Remove conversation logo
///
/// Throws in case of failure
static Future<void> removeLogo(int convID) async =>
static Future<void> removeLogo(int? convID) async =>
await APIRequest.withLogin("conversations/delete_image")
.addInt("convID", convID)
.execWithThrow();
/// Delete a conversation specified by its [id]
Future<void> deleteConversation(int id) async =>
Future<void> deleteConversation(int? id) async =>
await APIRequest.withLogin("conversations/delete")
.addInt("conversationID", id)
.execWithThrow();
@ -132,7 +131,7 @@ class ConversationsHelper {
await APIRequest.withLogin("conversations/getList").execWithThrow();
ConversationsList list = ConversationsList();
response.getArray().forEach((f) => list.add(apiToConversation(f)));
response.getArray()!.forEach((f) => list.add(apiToConversation(f)));
// Update the database
await ConversationsSerializationHelper().setList(list);
@ -148,7 +147,7 @@ class ConversationsHelper {
}
/// Get information about a single conversation specified by its [id]
Future<Conversation> _downloadSingle(int id) async {
Future<Conversation> _downloadSingle(int? id) async {
final response = await APIRequest(
uri: "conversations/get_single",
needLogin: true,
@ -167,7 +166,7 @@ class ConversationsHelper {
/// case of failure
///
/// Return value of this method is never null.
Future<Conversation> getSingle(int id, {bool force = false}) async {
Future<Conversation> getSingle(int? id, {bool force = false}) async {
if (force ||
!await ConversationsSerializationHelper().any((c) => c.id == id))
return await _downloadSingle(id);
@ -178,19 +177,19 @@ class ConversationsHelper {
/// Get the name of a [conversation]. This requires information
/// about the users of this conversation
static String getConversationName(
Conversation conversation, UsersList users) {
if (conversation.hasName) return conversation.name;
Conversation conversation, UsersList? users) {
if (conversation.hasName) return conversation.name!;
String name = "";
int count = 0;
for (int i = 0; i < 3 && i < conversation.members.length; i++)
if (conversation.members[i].userID != userID()) {
for (int i = 0; i < 3 && i < conversation.members!.length; i++)
if (conversation.members![i].userID != userID()) {
name += (count > 0 ? ", " : "") +
users.getUser(conversation.members[i].userID).fullName;
users!.getUser(conversation.members![i].userID).fullName;
count++;
}
if (conversation.members.length > 3) name += ", ...";
if (conversation.members!.length > 3) name += ", ...";
return name;
}
@ -200,7 +199,7 @@ class ConversationsHelper {
/// true
///
/// Throws an exception in case of failure
Future<int> getPrivate(int userID, {bool allowCreate = true}) async {
Future<int> getPrivate(int? userID, {bool allowCreate = true}) async {
final response = await APIRequest(
uri: "conversations/getPrivate",
needLogin: true,
@ -222,7 +221,7 @@ class ConversationsHelper {
/// Throws an exception in case of failure
static Future<String> getConversationNameAsync(
Conversation conversation) async {
if (conversation.hasName) return conversation.name;
if (conversation.hasName) return conversation.name!;
//Get information about the members of the conversation
final members = await UsersHelper().getList(conversation.membersID);
@ -273,7 +272,7 @@ class ConversationsHelper {
// Parse the response of the server
ConversationMessagesList list = ConversationMessagesList();
response.getArray().forEach((f) {
response.getArray()!.forEach((f) {
list.add(
apiToConversationMessage(f),
);
@ -294,7 +293,7 @@ class ConversationsHelper {
/// Throws an exception in case of failure
Future<ConversationMessagesList> _downloadNewMessagesSingle(
int conversationID,
{int lastMessageID = 0}) async {
{int? lastMessageID = 0}) async {
// Execute the request on the server
final response = await APIRequest(
uri: "conversations/refresh_single",
@ -311,8 +310,8 @@ class ConversationsHelper {
///
/// Throws in case of failure
Future<ConversationMessagesList> getOlderMessages({
@required int conversationID,
@required int oldestMessagesID,
required int conversationID,
required int? oldestMessagesID,
int limit = 15,
}) async {
// Perform the request online
@ -334,8 +333,8 @@ class ConversationsHelper {
///
/// Throws in case of failure
Future<ConversationMessagesList> getNewMessages(
{@required int conversationID,
int lastMessageID = 0,
{required int conversationID,
int? lastMessageID = 0,
bool online = true}) async {
if (online)
return await _downloadNewMessagesSingle(conversationID,
@ -348,8 +347,8 @@ class ConversationsHelper {
/// Send a new message to the server
Future<SendMessageResult> sendMessage(
NewConversationMessage message, {
ProgressCallback sendProgress,
CancelToken cancelToken,
ProgressCallback? sendProgress,
CancelToken? cancelToken,
}) async {
final request = APIRequest.withLogin("conversations/sendMessage")
.addInt("conversationID", message.conversationID)
@ -388,7 +387,7 @@ class ConversationsHelper {
await ConversationsMessagesSerializationHelper(msg.convID).remove(msg);
/// Update a message content
Future<bool> updateMessage(int id, String newContent) async {
Future<bool> updateMessage(int? id, String newContent) async {
final response = await APIRequest(
uri: "conversations/updateMessage",
needLogin: true,
@ -400,7 +399,7 @@ class ConversationsHelper {
}
/// Delete permanently a message specified by its [id]
Future<bool> deleteMessage(int id) async {
Future<bool> deleteMessage(int? id) async {
// Delete the message online
final response = await APIRequest(
uri: "conversations/deleteMessage",
@ -418,7 +417,7 @@ class ConversationsHelper {
static Future<UnreadConversationsList> getListUnread() async {
final list = (await APIRequest.withLogin("conversations/get_list_unread")
.execWithThrow())
.getArray();
.getArray()!;
return UnreadConversationsList()
..addAll(list.map((f) => UnreadConversation(
@ -431,7 +430,7 @@ class ConversationsHelper {
/// conversation through WebSocket
Future<void> registerConversationEvents(int id) async {
if (_registeredConversations.containsKey(id))
_registeredConversations[id]++;
_registeredConversations.update(id, (value) => value + 1);
else {
_registeredConversations[id] = 1;
await ws("\$main/register_conv", {"convID": id});
@ -442,16 +441,16 @@ class ConversationsHelper {
Future<void> unregisterConversationEvents(int id) async {
if (!_registeredConversations.containsKey(id)) return;
_registeredConversations[id]--;
_registeredConversations.update(id, (value) => value - 1);
if (_registeredConversations[id] <= 0) {
if (_registeredConversations[id]! <= 0) {
_registeredConversations.remove(id);
await ws("\$main/unregister_conv", {"convID": id});
}
}
/// Send a notification to inform that the user is writing a message
static Future<void> sendWritingEvent(int convID) async =>
static Future<void> sendWritingEvent(int? convID) async =>
await ws("conversations/is_writing", {"convID": convID});
/// Turn an API response into a ConversationMessage object

View File

@ -9,11 +9,11 @@ import 'package:sqflite/sqflite.dart';
/// @author Pierre HUBERT
abstract class DatabaseHelper {
static Database _db;
static Database? _db;
/// Open the database
static Future<void> open() async {
if (_db != null && _db.isOpen) return;
if (_db != null && _db!.isOpen) return;
var databasePath = await getDatabasesPath();
_db = await openDatabase(
@ -24,7 +24,7 @@ abstract class DatabaseHelper {
}
/// Get a database instance
static Future<Database> get() async {
static Future<Database?> get() async {
await open();
return _db;
}

View File

@ -18,12 +18,12 @@ abstract class ModelDatabaseHelper<T extends CacheModel> {
/// Insert an entry in the database
Future<void> _insertDB(T el) async {
await (await DatabaseHelper.get()).insert(tableName(), el.toMap());
await (await DatabaseHelper.get())!.insert(tableName(), el.toMap());
}
/// Update an element in the database
Future<void> _updateDB(T el) async {
await (await DatabaseHelper.get()).update(
await (await DatabaseHelper.get())!.update(
tableName(),
el.toMap(),
where: "${BaseTableContract.C_ID} = ?",
@ -34,14 +34,14 @@ abstract class ModelDatabaseHelper<T extends CacheModel> {
/// Get an element from the database with a specified [id]
///
/// Returns null if none found
Future<T> get(int id) async {
List<Map> maps = await (await DatabaseHelper.get()).query(
Future<T?> get(int id) async {
List<Map> maps = await (await DatabaseHelper.get())!.query(
tableName(),
where: '${BaseTableContract.C_ID} = ?',
whereArgs: [id],
);
if (maps.length > 0) return initializeFromMap(maps[0]);
if (maps.length > 0) return initializeFromMap(maps[0] as Map<String, dynamic>);
return null;
}
@ -50,7 +50,7 @@ abstract class ModelDatabaseHelper<T extends CacheModel> {
///
/// Return true if at least one entry was deleted / false else
Future<bool> delete(int id) async {
return await (await DatabaseHelper.get()).delete(
return await (await DatabaseHelper.get())!.delete(
tableName(),
where: '${BaseTableContract.C_ID} = ?',
whereArgs: [id],
@ -59,22 +59,22 @@ abstract class ModelDatabaseHelper<T extends CacheModel> {
/// Get all the entries from the table
Future<List<T>> getAll() async {
List<Map> maps = await (await DatabaseHelper.get()).query(tableName());
return maps.map((f) => initializeFromMap(f)).toList();
List<Map> maps = await (await DatabaseHelper.get())!.query(tableName());
return maps.map((f) => initializeFromMap(f as Map<String, dynamic>)).toList();
}
/// Get some entries from the table based on some conditions
Future<List<T>> getMultiple(
{bool distinct,
List<String> columns,
String where,
List<dynamic> whereArgs,
String groupBy,
String having,
String orderBy,
int limit,
int offset}) async {
List<Map> maps = await (await DatabaseHelper.get()).query(
{bool? distinct,
List<String>? columns,
String? where,
List<dynamic>? whereArgs,
String? groupBy,
String? having,
String? orderBy,
int? limit,
int? offset}) async {
List<Map> maps = await (await DatabaseHelper.get())!.query(
tableName(),
distinct: distinct,
columns: columns,
@ -86,12 +86,12 @@ abstract class ModelDatabaseHelper<T extends CacheModel> {
limit: limit,
offset: offset,
);
return maps.map((f) => initializeFromMap(f)).toList();
return maps.map((f) => initializeFromMap(f as Map<String, dynamic>)).toList();
}
/// Empty the table
Future<void> clearTable() async {
await (await DatabaseHelper.get()).execute("DELETE FROM ${tableName()}");
await (await DatabaseHelper.get())!.execute("DELETE FROM ${tableName()}");
}
/// Check out whether an element specified with its [id] is present

View File

@ -17,14 +17,14 @@ class WSClosedEvent {}
/// New number of notifications
class NewNumberNotifsEvent {
final int newNum;
final int? newNum;
NewNumberNotifsEvent(this.newNum);
}
/// New number of unread conversations
class NewNumberUnreadConversations {
final int newNum;
final int? newNum;
NewNumberUnreadConversations(this.newNum);
}
@ -45,15 +45,15 @@ class UpdatedCommentEvent {
/// Deleted comment
class DeletedCommentEvent {
final int commentID;
final int? commentID;
DeletedCommentEvent(this.commentID);
}
/// Writing message in conversation event
class WritingMessageInConversationEvent {
final int convID;
final int userID;
final int? convID;
final int? userID;
WritingMessageInConversationEvent(this.convID, this.userID);
}
@ -81,31 +81,31 @@ class DeletedConversationMessageEvent {
/// Remove user from conversation
class RemovedUserFromConversationEvent {
final int convID;
final int userID;
final int? convID;
final int? userID;
RemovedUserFromConversationEvent(this.convID, this.userID);
}
/// Deleted conversation
class DeletedConversationEvent {
final int convID;
final int? convID;
DeletedConversationEvent(this.convID);
}
/// User joined call event
class UserJoinedCallEvent {
final int callID;
final int userID;
final int? callID;
final int? userID;
UserJoinedCallEvent(this.callID, this.userID);
}
/// User left call event
class UserLeftCallEvent {
final int callID;
final int userID;
final int? callID;
final int? userID;
UserLeftCallEvent(this.callID, this.userID);
}
@ -114,38 +114,36 @@ class UserLeftCallEvent {
class NewCallSignalEvent {
final int callID;
final int peerID;
final RTCSessionDescription sessionDescription;
final RTCIceCandidate candidate;
final RTCSessionDescription? sessionDescription;
final RTCIceCandidate? candidate;
const NewCallSignalEvent({
this.callID,
this.peerID,
required this.callID,
required this.peerID,
this.sessionDescription,
this.candidate,
}) : assert(callID != null),
assert(peerID != null),
assert(sessionDescription != null || candidate != null);
}) : assert(sessionDescription != null || candidate != null);
}
/// Call peer ready event
class CallPeerReadyEvent {
final int callID;
final int peerID;
final int? callID;
final int? peerID;
CallPeerReadyEvent(this.callID, this.peerID);
}
/// Call peer interrupted streaming event
class CallPeerInterruptedStreamingEvent {
final int callID;
final int peerID;
final int? callID;
final int? peerID;
CallPeerInterruptedStreamingEvent(this.callID, this.peerID);
}
/// Call closed event
class CallClosedEvent {
final int callID;
final int? callID;
CallClosedEvent(this.callID);
}

View File

@ -20,7 +20,7 @@ class FirebaseMessagingHelper {
}
/// Get a Firebase access token
static Future<String> getToken() async {
static Future<String?> getToken() async {
return await FirebaseMessaging.instance.getToken();
}
}

View File

@ -11,7 +11,7 @@ import 'package:comunic/models/group.dart';
class ForezGroupsHelper {
static Future<List<Group>> getForezGroups() async {
return (await APIRequest.withLogin("forez/get_groups").execWithThrow())
.getArray()
.getArray()!
.cast<Map<String, dynamic>>()
.map(GroupsHelper.getGroupFromAPI)
.toList();
@ -21,10 +21,10 @@ class ForezGroupsHelper {
///
/// This methods throws an exception in case of failure
static Future<AdvancedUserInfo> getMemberInfo(int groupID, int userID) async {
final response = await APIRequest.withLogin("forez/get_member_info")
final response = await (APIRequest.withLogin("forez/get_member_info")
.addInt("group", groupID)
.addInt("user", userID)
.execWithThrowGetObject();
.execWithThrowGetObject());
return UsersHelper.apiToAdvancedUserInfo(response);
}

View File

@ -6,8 +6,8 @@ import 'package:comunic/models/forez_presence.dart';
///
/// @author Pierre Hubert
int _cachedGroup;
PresenceSet _cache;
int? _cachedGroup;
PresenceSet? _cache;
class ForezPresenceHelper {
/// Refresh presence cache
@ -40,16 +40,16 @@ class ForezPresenceHelper {
/// Get the presences of a given user
///
/// Throws in case of failure
static Future<PresenceSet> getForUser(int groupID, int userID) async {
static Future<PresenceSet> getForUser(int groupID, int? userID) async {
await _checkCache(groupID);
return _cache.getForUser(userID);
return _cache!.getForUser(userID);
}
/// Get all the available presences
///
/// Throws in case of failure
static Future<PresenceSet> getAll(int groupID) async {
static Future<PresenceSet?> getAll(int groupID) async {
await _checkCache(groupID);
return _cache;
}

View File

@ -3,7 +3,6 @@ import 'package:comunic/lists/friends_list.dart';
import 'package:comunic/models/api_request.dart';
import 'package:comunic/models/friend.dart';
import 'package:comunic/models/friend_status.dart';
import 'package:meta/meta.dart';
/// Friends helper
///
@ -16,7 +15,7 @@ class FriendsHelper {
///
/// Returns the list of friends in case of success, or null if an error
/// occurred
Future<FriendsList> _downloadList() async {
Future<FriendsList?> _downloadList() async {
final response = await APIRequest(
uri: "friends/getList",
needLogin: true,
@ -30,7 +29,7 @@ class FriendsHelper {
// Parse and return the list of friends
FriendsList list = FriendsList()
..addAll(response
.getArray()
.getArray()!
.cast<Map<String, dynamic>>()
.map(apiToFriend)
.toList());
@ -54,7 +53,7 @@ class FriendsHelper {
}
/// Get the list, either from an online or an offline source
Future<FriendsList> getList({@required bool online}) async {
Future<FriendsList?> getList({required bool online}) async {
if (online)
return await _downloadList();
else
@ -132,7 +131,7 @@ class FriendsHelper {
if (response.code != 200)
throw new Exception("Could not get the list of friends of this user!");
return Set<int>.from(response.getArray());
return Set<int>.from(response.getArray()!);
}
/// Send a friendship request to a specified user

View File

@ -49,15 +49,15 @@ enum GetAdvancedInfoStatus { SUCCESS, ACCESS_DENIED }
class GetAdvancedInfoResult {
final GetAdvancedInfoStatus status;
final AdvancedGroupInfo info;
final AdvancedGroupInfo? info;
GetAdvancedInfoResult(this.status, this.info) : assert(status != null);
GetAdvancedInfoResult(this.status, this.info);
}
/// Groups helper
class GroupsHelper {
/// Download a list of groups information from the server
Future<GroupsList> _downloadList(Set<int> groups) async {
Future<GroupsList?> _downloadList(Set<int?> groups) async {
final response = await APIRequest(
uri: "groups/get_multiple_info",
needLogin: true,
@ -77,7 +77,7 @@ class GroupsHelper {
/// Get a list of groups from the server. In case of error, this method throws
/// an exception
Future<GroupsList> getListOrThrow(Set<int> groups,
Future<GroupsList> getListOrThrow(Set<int?> groups,
{bool force = false}) async {
final list = await getList(groups, force: force);
@ -87,11 +87,11 @@ class GroupsHelper {
}
/// Get a list of groups from the server
Future<GroupsList> getList(Set<int> groups, {bool force = false}) async {
Future<GroupsList?> getList(Set<int?> groups, {bool force = false}) async {
final list = GroupsList();
// Check which groups information to download
final toDownload = Set<int>();
final toDownload = Set<int?>();
groups.forEach((groupID) {
if (!force && _groupsListCache.containsKey(groupID))
list[groupID] = _groupsListCache[groupID];
@ -122,10 +122,10 @@ class GroupsHelper {
}
/// Get the list of groups of a user
Future<Set<int>> getListUser() async =>
Future<Set<int?>> getListUser() async =>
(await APIRequest(uri: "groups/get_my_list", needLogin: true).exec())
.assertOk()
.getArray()
.getArray()!
.map((f) => cast<int>(f))
.toSet();
@ -142,7 +142,7 @@ class GroupsHelper {
/// Perform a simple membership request
static Future<bool> _simpleMembershipRequest(int groupID, String uri,
{Map<String, String> args}) async =>
{Map<String, String>? args}) async =>
(await (APIRequest.withLogin(uri)
..addInt("id", groupID)
..addArgs(args == null ? Map() : args))
@ -176,7 +176,7 @@ class GroupsHelper {
.isOK;
/// Get advanced information about the user
Future<GetAdvancedInfoResult> getAdvancedInfo(int groupID) async {
Future<GetAdvancedInfoResult> getAdvancedInfo(int? groupID) async {
// Get advanced information about the user
final result =
await (APIRequest(uri: "groups/get_advanced_info", needLogin: true)
@ -202,7 +202,7 @@ class GroupsHelper {
/// change in the future
///
/// Throws in case of error
Future<AdvancedGroupInfo> getSettings(int groupID) async {
Future<AdvancedGroupInfo?> getSettings(int groupID) async {
final groupInfo = await getAdvancedInfo(groupID);
if (groupInfo.status != GetAdvancedInfoStatus.SUCCESS)
@ -239,7 +239,7 @@ class GroupsHelper {
"posts_level",
invertMap(
_APIGroupsPostsCreationLevelsMap)[settings.postCreationLevel])
.addBool("is_members_list_public", settings.isMembersListPublic)
.addBool("is_members_list_public", settings.isMembersListPublic!)
.addString("description", settings.description)
.addString("url", settings.url)
.execWithThrow();
@ -248,7 +248,7 @@ class GroupsHelper {
/// Upload a new logo
///
/// Throws in case of failure
static Future<void> uploadNewLogo(int groupID, Uint8List bytes) async =>
static Future<void> uploadNewLogo(int groupID, Uint8List? bytes) async =>
await APIRequest(uri: "groups/upload_logo", needLogin: true)
.addInt("id", groupID)
.addBytesFile("logo", BytesFile("logo.png", bytes))
@ -279,7 +279,7 @@ class GroupsHelper {
..addAll((await APIRequest(uri: "groups/get_members", needLogin: true)
.addInt("id", groupID)
.execWithThrow())
.getArray()
.getArray()!
.map((f) => _apiToGroupMembership(f))
.toList());
@ -295,7 +295,7 @@ class GroupsHelper {
/// Cancel a group membership invitation
///
/// Throws an exception in case of failure
static Future<void> cancelInvitation(int groupID, int userID) async =>
static Future<void> cancelInvitation(int groupID, int? userID) async =>
await APIRequest.withLogin("groups/cancel_invitation")
.addInt("groupID", groupID)
.addInt("userID", userID)
@ -305,7 +305,7 @@ class GroupsHelper {
///
/// Throws an exception in case of failure
static Future<void> respondRequest(
int groupID, int userID, bool accept) async =>
int groupID, int? userID, bool accept) async =>
await APIRequest.withLogin("groups/respond_request")
.addInt("groupID", groupID)
.addInt("userID", userID)
@ -351,7 +351,7 @@ class GroupsHelper {
///
/// Throws in case of failure
static Future<void> setConversationVisibility(
int convID, GroupMembershipLevel newLevel) async =>
int? convID, GroupMembershipLevel? newLevel) async =>
await APIRequest.withLogin("groups/set_conversation_visibility")
.addInt("conv_id", convID)
.addString(
@ -364,7 +364,7 @@ class GroupsHelper {
/// Delete a group's conversation
///
/// Throws in case of failure
static Future<void> deleteConversation(int convID) async =>
static Future<void> deleteConversation(int? convID) async =>
await APIRequest.withLogin("groups/delete_conversation")
.addInt("conv_id", convID)
.execWithThrow();
@ -376,11 +376,11 @@ class GroupsHelper {
name: map["name"],
iconURL: map["icon_url"],
numberMembers: map["number_members"],
membershipLevel: APIGroupsMembershipLevelsMap[map["membership"]],
visibilityLevel: _APIGroupsVisibilityLevelsMap[map["visibility"]],
membershipLevel: APIGroupsMembershipLevelsMap[map["membership"]]!,
visibilityLevel: _APIGroupsVisibilityLevelsMap[map["visibility"]]!,
registrationLevel:
_APIGroupsRegistrationLevelsMap[map["registration_level"]],
postCreationLevel: _APIGroupsPostsCreationLevelsMap[map["posts_level"]],
_APIGroupsRegistrationLevelsMap[map["registration_level"]]!,
postCreationLevel: _APIGroupsPostsCreationLevelsMap[map["posts_level"]]!,
virtualDirectory: nullToEmpty(map["virtual_directory"]),
following: map["following"]);
}
@ -392,11 +392,11 @@ class GroupsHelper {
name: map["name"],
iconURL: map["icon_url"],
numberMembers: map["number_members"],
membershipLevel: APIGroupsMembershipLevelsMap[map["membership"]],
visibilityLevel: _APIGroupsVisibilityLevelsMap[map["visibility"]],
membershipLevel: APIGroupsMembershipLevelsMap[map["membership"]]!,
visibilityLevel: _APIGroupsVisibilityLevelsMap[map["visibility"]]!,
registrationLevel:
_APIGroupsRegistrationLevelsMap[map["registration_level"]],
postCreationLevel: _APIGroupsPostsCreationLevelsMap[map["posts_level"]],
_APIGroupsRegistrationLevelsMap[map["registration_level"]]!,
postCreationLevel: _APIGroupsPostsCreationLevelsMap[map["posts_level"]]!,
isMembersListPublic: map["is_members_list_public"],
virtualDirectory: nullToEmpty(map["virtual_directory"]),
following: map["following"],
@ -418,6 +418,6 @@ class GroupsHelper {
userID: row["user_id"],
groupID: row["group_id"],
timeCreate: row["time_create"],
level: APIGroupsMembershipLevelsMap[row["level"]],
level: APIGroupsMembershipLevelsMap[row["level"]]!,
);
}

View File

@ -29,7 +29,7 @@ class IndependentPushNotificationsHelper {
}
/// Configure independent push notification services with a pull URL
static Future<void> configure(String wsURL) async {
static Future<void> configure(String? wsURL) async {
await platform.invokeMethod("configure", wsURL);
}

View File

@ -1,6 +1,5 @@
import 'package:comunic/enums/likes_type.dart';
import 'package:comunic/helpers/websocket_helper.dart';
import 'package:meta/meta.dart';
/// Likes helper
///
@ -16,9 +15,9 @@ const LikesAPIMap = {
class LikesHelper {
/// Update liking status of an element
Future<void> setLiking({
@required LikesType type,
@required bool like,
@required int id,
required LikesType type,
required bool like,
required int id,
}) async {
return (await ws("likes/update", {
"type": LikesAPIMap[type],

View File

@ -75,15 +75,15 @@ class NotificationsHelper {
// Parse the list of notifications
return NotificationsList()
..addAll(response
.getArray()
.getArray()!
.map((f) => Notification(
id: f["id"],
timeCreate: f["time_create"],
seen: f["seen"],
fromUser: f["from_user_id"],
onElemId: f["on_elem_id"],
onElemType: _NotificationElementTypeAPImapping[f["on_elem_type"]],
type: _NotificationsTypeAPImapping[f["type"]],
onElemType: _NotificationElementTypeAPImapping[f["on_elem_type"]]!,
type: _NotificationsTypeAPImapping[f["type"]]!,
fromContainerId: f["from_container_id"],
fromContainerType: f["from_container_type"] == ""
? null

View File

@ -55,7 +55,7 @@ class PostsHelper {
/// Get the list of latest posts. Return the list of posts or null in case of
/// failure
Future<PostsList> getLatest({int from = 0}) async {
Future<PostsList?> getLatest({int from = 0}) async {
final response =
await APIRequest(uri: "posts/get_latest", needLogin: true, args: {
"include_groups": true.toString(),
@ -66,7 +66,8 @@ class PostsHelper {
try {
// Parse & return the list of posts
return PostsList()..addAll(response.getArray().map((f) => _apiToPost(f)));
return PostsList()
..addAll(response.getArray()!.map((f) => _apiToPost(f)));
} catch (e) {
print(e.toString());
return null;
@ -74,7 +75,7 @@ class PostsHelper {
}
/// Get the list of posts of a user
Future<PostsList> getUserPosts(int userID, {int from = 0}) async {
Future<PostsList?> getUserPosts(int? userID, {int from = 0}) async {
final response = await (APIRequest(uri: "posts/get_user", needLogin: true)
..addInt("userID", userID)
..addInt("startFrom", from == 0 ? 0 : from - 1))
@ -84,7 +85,8 @@ class PostsHelper {
try {
// Parse & return the list of posts
return PostsList()..addAll(response.getArray().map((f) => _apiToPost(f)));
return PostsList()
..addAll(response.getArray()!.map((f) => _apiToPost(f)));
} catch (e) {
print(e.toString());
return null;
@ -92,7 +94,7 @@ class PostsHelper {
}
/// Get the list of posts of a group
Future<PostsList> getGroupPosts(int groupID, {int from = 0}) async {
Future<PostsList?> getGroupPosts(int groupID, {int from = 0}) async {
final response = await (APIRequest(uri: "posts/get_group", needLogin: true)
..addInt("groupID", groupID)
..addInt("startFrom", from == 0 ? 0 : from - 1))
@ -102,7 +104,8 @@ class PostsHelper {
try {
// Parse & return the list of posts
return PostsList()..addAll(response.getArray().map((f) => _apiToPost(f)));
return PostsList()
..addAll(response.getArray()!.map((f) => _apiToPost(f)));
} catch (e) {
print(e.toString());
return null;
@ -158,13 +161,14 @@ class PostsHelper {
case PostKind.COUNTDOWN:
request.addInt(
"time-end", (post.timeEnd.millisecondsSinceEpoch / 1000).floor());
"time-end", (post.timeEnd!.millisecondsSinceEpoch / 1000).floor());
break;
case PostKind.SURVEY:
request.addString("question", post.survey.question);
request.addString("answers", post.survey.answers.join("<>"));
request.addBool("allowNewAnswers", post.survey.allowNewChoicesCreation);
request.addString("question", post.survey!.question);
request.addString("answers", post.survey!.answers.join("<>"));
request.addBool(
"allowNewAnswers", post.survey!.allowNewChoicesCreation);
break;
case PostKind.YOUTUBE:
@ -173,7 +177,6 @@ class PostsHelper {
default:
throw Exception("Unsupported post type :" + post.kind.toString());
break;
}
final response = await request.execWithFiles();
@ -221,7 +224,7 @@ class PostsHelper {
/// Register to a post events
Future<void> registerPostEvents(int id) async {
if (_registeredPosts.containsKey(id))
_registeredPosts[id]++;
_registeredPosts.update(id, (v) => v + 1);
else {
_registeredPosts[id] = 1;
await ws("\$main/register_post", {"postID": id});
@ -232,9 +235,9 @@ class PostsHelper {
Future<void> unregisterPostEvents(int id) async {
if (!_registeredPosts.containsKey(id)) return;
_registeredPosts[id]--;
_registeredPosts.update(id, (v) => v - 1);
if (_registeredPosts[id] <= 0) {
if (_registeredPosts[id]! <= 0) {
_registeredPosts.remove(id);
await ws("\$main/unregister_post", {"postID": id});
}
@ -242,14 +245,14 @@ class PostsHelper {
/// Turn an API entry into a [Post] object
Post _apiToPost(Map<String, dynamic> map) {
final postKind = _APIPostsKindsMap[map["kind"]];
final postKind = _APIPostsKindsMap[map["kind"]]!;
// Parse comments
CommentsList comments;
CommentsList? comments;
if (map["comments"] != null) {
comments = CommentsList();
map["comments"]
.forEach((v) => comments.add(CommentsHelper.apiToComment(v)));
.forEach((v) => comments!.add(CommentsHelper.apiToComment(v)));
}
final survey = postKind == PostKind.SURVEY
@ -263,7 +266,7 @@ class PostsHelper {
groupID: map["group_id"],
timeSent: map["post_time"],
content: DisplayedString(map["content"]),
visibilityLevel: _APIPostsVisibilityLevelMap[map["visibility_level"]],
visibilityLevel: _APIPostsVisibilityLevelMap[map["visibility_level"]]!,
kind: postKind,
fileSize: map["file_size"],
fileType: map["file_type"],
@ -276,7 +279,7 @@ class PostsHelper {
linkImage: map["link_image"],
likes: map["likes"],
userLike: map["userlike"],
access: _APIUserAccessMap[map["user_access"]],
access: _APIUserAccessMap[map["user_access"]]!,
comments: comments,
survey: survey);
}

View File

@ -30,7 +30,7 @@ const _PreferenceKeysName = {
};
class PreferencesHelper {
static PreferencesHelper _instance;
static PreferencesHelper? _instance;
static Future<PreferencesHelper> getInstance() async {
if (_instance == null) {
@ -38,10 +38,10 @@ class PreferencesHelper {
await _init();
}
return _instance;
return _instance!;
}
static SharedPreferences _sharedPreferences;
static late SharedPreferences _sharedPreferences;
PreferencesHelper._();
@ -50,7 +50,7 @@ class PreferencesHelper {
}
/// Set new login tokens
Future<void> setLoginToken(String token) async {
Future<void> setLoginToken(String? token) async {
if (token != null)
await setString(PreferencesKeyList.LOGIN_TOKEN, token);
else
@ -58,7 +58,7 @@ class PreferencesHelper {
}
/// Get current [LoginTokens]. Returns null if none or in case of failure
String getLoginToken() {
String? getLoginToken() {
try {
final string = getString(PreferencesKeyList.LOGIN_TOKEN);
return string;
@ -69,35 +69,35 @@ class PreferencesHelper {
}
bool containsKey(PreferencesKeyList key) {
return _sharedPreferences.containsKey(_PreferenceKeysName[key]);
return _sharedPreferences.containsKey(_PreferenceKeysName[key]!);
}
Future<bool> removeKey(PreferencesKeyList key) async {
return await _sharedPreferences.remove(_PreferenceKeysName[key]);
return await _sharedPreferences.remove(_PreferenceKeysName[key]!);
}
Future<bool> setString(PreferencesKeyList key, String value) async {
return await _sharedPreferences.setString(_PreferenceKeysName[key], value);
return await _sharedPreferences.setString(_PreferenceKeysName[key]!, value);
}
String getString(PreferencesKeyList key) {
return _sharedPreferences.getString(_PreferenceKeysName[key]);
String? getString(PreferencesKeyList key) {
return _sharedPreferences.getString(_PreferenceKeysName[key]!);
}
Future<bool> setBool(PreferencesKeyList key, bool value) async {
return await _sharedPreferences.setBool(_PreferenceKeysName[key], value);
return await _sharedPreferences.setBool(_PreferenceKeysName[key]!, value);
}
Future<bool> setInt(PreferencesKeyList key, int value) async {
return await _sharedPreferences.setInt(_PreferenceKeysName[key], value);
return await _sharedPreferences.setInt(_PreferenceKeysName[key]!, value);
}
int getInt(PreferencesKeyList key) {
return _sharedPreferences.getInt(_PreferenceKeysName[key]);
int? getInt(PreferencesKeyList key) {
return _sharedPreferences.getInt(_PreferenceKeysName[key]!);
}
bool getBool(PreferencesKeyList key, {bool alternative = false}) {
final v = _sharedPreferences.getBool(_PreferenceKeysName[key]);
final v = _sharedPreferences.getBool(_PreferenceKeysName[key]!);
return v == null ? alternative : v;
}
@ -115,7 +115,7 @@ class PreferencesHelper {
}
}
PreferencesHelper preferences() {
PreferencesHelper? preferences() {
if (PreferencesHelper._instance == null)
throw Exception("Try to get preference before their initialization!");

View File

@ -21,14 +21,14 @@ const _PushNotificationsAPIMap = {
class PushNotificationsHelper {
/// Get cached status of push notifications
static Future<PushNotificationsStatus> getLocalStatus() async {
static Future<PushNotificationsStatus?> getLocalStatus() async {
final pref = await PreferencesHelper.getInstance();
if (!pref.containsKey(PreferencesKeyList.PUSH_NOTIFICATIONS_STATUS))
return PushNotificationsStatus.UNDEFINED;
return _PushNotificationsAPIMap[
pref.getString(PreferencesKeyList.PUSH_NOTIFICATIONS_STATUS)];
pref.getString(PreferencesKeyList.PUSH_NOTIFICATIONS_STATUS)!];
}
/// Refresh local status with information from server
@ -62,8 +62,8 @@ class PushNotificationsHelper {
/// Configure push notifications
static Future<void> configure(
BuildContext context, PushNotificationsStatus newStatus) async {
String firebaseToken = "";
BuildContext context, PushNotificationsStatus? newStatus) async {
String? firebaseToken = "";
switch (newStatus) {
case PushNotificationsStatus.DISABLED:
break;
@ -90,8 +90,8 @@ class PushNotificationsHelper {
/// Set new push notification status on the server
static Future<void> setNewStatus(
PushNotificationsStatus newStatus, {
String firebaseToken = "",
PushNotificationsStatus? newStatus, {
String? firebaseToken = "",
}) async =>
await APIRequest.withLogin("push_notifications/configure")
.addString(
@ -104,6 +104,6 @@ class PushNotificationsHelper {
/// Is true if possible if push notifications are configurable
static bool get arePushNotificationsAvailable =>
srvConfig.notificationsPolicy.hasFirebase ||
(isAndroid && srvConfig.notificationsPolicy.hasIndependent);
srvConfig!.notificationsPolicy.hasFirebase ||
(isAndroid && srvConfig!.notificationsPolicy.hasIndependent);
}

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

@ -13,7 +13,7 @@ class SearchHelper {
/// Search for user. This method returns information about the target users
///
/// Returns information about the target users or null if an error occurred
Future<UsersList> searchUser(String query) async {
Future<UsersList?> searchUser(String query) async {
// Execute the query on the server
final response = await APIRequest(
uri: "user/search", needLogin: true, args: {"query": query}).exec();
@ -21,7 +21,7 @@ class SearchHelper {
if (response.code != 200) return null;
return await UsersHelper()
.getUsersInfo(response.getArray().map((f) => cast<int>(f)).toList());
.getUsersInfo(response.getArray()!.map((f) => cast<int>(f)).toList());
}
/// Perform a global search
@ -31,7 +31,7 @@ class SearchHelper {
result.assertOk();
return SearchResultsList()..addAll(result.getArray().map((f) {
return SearchResultsList()..addAll(result.getArray()!.map((f) {
switch (f["kind"]) {
case "user":
return SearchResult(id: f["id"], kind: SearchResultKind.USER);

View File

@ -17,7 +17,7 @@ abstract class SerializableElement<T> extends Comparable<T> {
abstract class BaseSerializationHelper<T extends SerializableElement> {
/// List cache
List<T> _cache;
List<T>? _cache;
/// The name of the type of data to serialise
String get type;
@ -48,12 +48,15 @@ abstract class BaseSerializationHelper<T extends SerializableElement> {
try {
final file = await _getFilePath();
if (!await file.exists()) return _cache = [];
if (!await file.exists()) {
_cache = [];
return;
}
final List<dynamic> json = jsonDecode(await file.readAsString());
_cache = json.cast<Map<String, dynamic>>().map(parse).toList();
_cache.sort();
_cache!.sort();
} catch (e, s) {
logError(e, s);
print("Failed to read serialized data!");
@ -67,8 +70,10 @@ abstract class BaseSerializationHelper<T extends SerializableElement> {
try {
final file = await _getFilePath();
await file.writeAsString(jsonEncode(
_cache.map((e) => e.toJson()).toList().cast<Map<String, dynamic>>()));
await file.writeAsString(jsonEncode(_cache!
.map((e) => e.toJson())
.toList()
.cast<Map<String, dynamic>>()));
} catch (e, s) {
print("Failed to write file!");
logError(e, s);
@ -78,7 +83,7 @@ abstract class BaseSerializationHelper<T extends SerializableElement> {
/// Get the current list of elements
Future<List<T>> getList() async {
await _loadCache();
return List.from(_cache);
return List.from(_cache!);
}
/// Set a new list of conversations
@ -90,23 +95,23 @@ abstract class BaseSerializationHelper<T extends SerializableElement> {
/// Insert new element
Future<void> insert(T el) async {
await _loadCache();
_cache.add(el);
_cache.sort();
_cache!.add(el);
_cache!.sort();
await _saveCache();
}
/// Insert new element
Future<void> insertMany(List<T> els) async {
await _loadCache();
_cache.addAll(els);
_cache.sort();
_cache!.addAll(els);
_cache!.sort();
await _saveCache();
}
/// Check if any entry in the last match the predicate
Future<bool> any(bool isContained(T t)) async {
await _loadCache();
return _cache.any((element) => isContained(element));
return _cache!.any((element) => isContained(element));
}
Future<bool> has(T el) => any((t) => t == el);
@ -114,7 +119,7 @@ abstract class BaseSerializationHelper<T extends SerializableElement> {
/// Check if any entry in the last match the predicate
Future<T> first(bool filter(T t)) async {
await _loadCache();
return _cache.firstWhere((element) => filter(element));
return _cache!.firstWhere((element) => filter(element));
}
/// Replace an element with another one
@ -122,10 +127,10 @@ abstract class BaseSerializationHelper<T extends SerializableElement> {
await _loadCache();
// Insert or replace the element
_cache = _cache.where((element) => !isToReplace(element)).toList();
_cache.add(newEl);
_cache = _cache!.where((element) => !isToReplace(element)).toList();
_cache!.add(newEl);
_cache.sort();
_cache!.sort();
await _saveCache();
}
@ -133,8 +138,8 @@ abstract class BaseSerializationHelper<T extends SerializableElement> {
Future<void> insertOrReplaceElements(List<T> list) async {
await _loadCache();
_cache.removeWhere((element) => list.any((newEl) => element == newEl));
_cache.addAll(list);
_cache!.removeWhere((element) => list.any((newEl) => element == newEl));
_cache!.addAll(list);
await _saveCache();
}
@ -142,7 +147,7 @@ abstract class BaseSerializationHelper<T extends SerializableElement> {
/// Remove elements
Future<void> removeElement(bool isToRemove(T t)) async {
await _loadCache();
_cache.removeWhere((element) => isToRemove(element));
_cache!.removeWhere((element) => isToRemove(element));
await _saveCache();
}

View File

@ -8,23 +8,22 @@ import 'package:comunic/models/conversation_message.dart';
///
/// @author Pierre Hubert
HashMap<int, ConversationsMessagesSerializationHelper> _instances;
HashMap<int?, ConversationsMessagesSerializationHelper>? _instances;
class ConversationsMessagesSerializationHelper
extends BaseSerializationHelper<ConversationMessage> {
final int convID;
ConversationsMessagesSerializationHelper._(int convID)
: convID = convID,
assert(convID != null);
: convID = convID;
factory ConversationsMessagesSerializationHelper(int convID) {
factory ConversationsMessagesSerializationHelper(int? convID) {
if (_instances == null) _instances = HashMap();
if (!_instances.containsKey(convID))
_instances[convID] = ConversationsMessagesSerializationHelper._(convID);
if (!_instances!.containsKey(convID))
_instances![convID] = ConversationsMessagesSerializationHelper._(convID!);
return _instances[convID];
return _instances![convID]!;
}
@override

View File

@ -28,5 +28,5 @@ class ConversationsSerializationHelper
ConversationsList()..addAll(await super.getList());
/// Get a conversation
Future<Conversation> get(int id) => first((t) => t.id == id);
Future<Conversation> get(int? id) => first((t) => t.id == id);
}

View File

@ -5,7 +5,7 @@ import 'package:comunic/models/user.dart';
///
/// @author Pierre Hubert
UsersListSerialisationHelper _singleton;
UsersListSerialisationHelper? _singleton;
class UsersListSerialisationHelper extends BaseSerializationHelper<User> {
UsersListSerialisationHelper._();
@ -13,7 +13,7 @@ class UsersListSerialisationHelper extends BaseSerializationHelper<User> {
factory UsersListSerialisationHelper() {
if (_singleton == null) _singleton = UsersListSerialisationHelper._();
return _singleton;
return _singleton!;
}
@override
@ -23,6 +23,6 @@ class UsersListSerialisationHelper extends BaseSerializationHelper<User> {
User parse(Map<String, dynamic> m) => User.fromJson(m);
/// Remove a user by its ID
Future<void> removeUserByID(int userID) =>
Future<void> removeUserByID(int? userID) =>
removeElement((t) => t.id == userID);
}

View File

@ -7,7 +7,7 @@ import 'package:version/version.dart';
/// @author Pierre Hubert
class ServerConfigurationHelper {
static ServerConfig _config;
static ServerConfig? _config;
/// Make sure the configuration has been correctly loaded
static Future<void> ensureLoaded() async {
@ -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,11 +95,20 @@ 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"],
));
}
/// Get current server configuration, throwing if it is not loaded yet
static ServerConfig get config {
static ServerConfig? get config {
if (_config == null)
throw Exception(
"Trying to access server configuration but it is not loaded yet!");
@ -107,6 +118,6 @@ class ServerConfigurationHelper {
}
/// 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

@ -94,7 +94,7 @@ class SettingsHelper {
hasImage: response["has_image"],
imageURL: response["image_url"],
visibility:
_APIAccountImageVisibilityAPILevels[response["visibility"]]);
_APIAccountImageVisibilityAPILevels[response["visibility"]]!);
}
/// Upload a new account image
@ -143,7 +143,7 @@ class SettingsHelper {
/// Delete a custom emoji
///
/// Throws in case of failure
static Future<void> deleteCustomEmoji(int emojiID) async =>
static Future<void> deleteCustomEmoji(int? emojiID) async =>
(await APIRequest(uri: "settings/delete_custom_emoji", needLogin: true)
.addInt("emojiID", emojiID)
.exec())
@ -223,7 +223,7 @@ class SettingsHelper {
///
/// Throws in case of failure
static Future<void> setDataConservationPolicy(
String password, DataConservationPolicySettings newSettings) async {
String? password, DataConservationPolicySettings newSettings) async {
await APIRequest(
uri: "settings/set_data_conservation_policy", needLogin: true)
.addString("password", password)

View File

@ -1,7 +1,6 @@
import 'package:comunic/models/api_request.dart';
import 'package:comunic/models/survey.dart';
import 'package:comunic/models/survey_choice.dart';
import 'package:meta/meta.dart';
/// Survey helper
///
@ -26,9 +25,7 @@ class SurveyHelper {
/// Send the response of a user to a survey
Future<bool> respondToSurvey(
{@required Survey survey, @required SurveyChoice choice}) async {
assert(survey != null);
assert(choice != null);
{required Survey survey, required SurveyChoice choice}) async {
return (await APIRequest(
uri: "surveys/send_response",

View File

@ -23,7 +23,7 @@ enum GetUserAdvancedInformationErrorCause {
class GetUserAdvancedUserError extends Error {
final GetUserAdvancedInformationErrorCause cause;
GetUserAdvancedUserError(this.cause) : assert(cause != null);
GetUserAdvancedUserError(this.cause);
}
class UsersHelper {
@ -31,7 +31,7 @@ class UsersHelper {
///
/// Return the list of users information in case of success, null in case of
/// failure
Future<UsersList> _downloadInfo(List<int> users) async {
Future<UsersList?> _downloadInfo(List<int?> users) async {
// Execute the request
final response = await APIRequest(
uri: "user/getInfoMultiple",
@ -69,7 +69,7 @@ class UsersHelper {
/// Get users information from a given [Set]. Throws an exception in case
/// of failure
Future<UsersList> getListWithThrow(Set<int> users,
Future<UsersList> getListWithThrow(Set<int?> users,
{bool forceDownload = false}) async {
final list =
await getUsersInfo(users.toList(), forceDownload: forceDownload);
@ -82,16 +82,16 @@ class UsersHelper {
}
/// Get information about a single user. Throws in case of failure
Future<User> getSingleWithThrow(int user,
Future<User> getSingleWithThrow(int? user,
{bool forceDownload = false}) async {
return (await getListWithThrow(Set<int>()..add(user),
return (await getListWithThrow(Set<int?>()..add(user),
forceDownload: forceDownload))[0];
}
/// Get users information from a given [Set]
///
/// Throws in case of failure
Future<UsersList> getList(Set<int> users,
Future<UsersList> getList(Set<int?> users,
{bool forceDownload = false}) async {
final list = await getUsersInfo(users.toList());
@ -104,13 +104,13 @@ class UsersHelper {
///
/// If [forceDownload] is set to true, the data will always be retrieved from
/// the server, otherwise cached data will be used if available
Future<UsersList> getUsersInfo(List<int> users,
Future<UsersList?> getUsersInfo(List<int?> users,
{bool forceDownload = false}) async {
List<int> toDownload = [];
List<int?> toDownload = [];
UsersList list = UsersList();
// Check cache
for (int userID in users) {
for (int? userID in users) {
if (!forceDownload &&
await UsersListSerialisationHelper().any((u) => u.id == userID))
list.add(

View File

@ -1,5 +1,5 @@
import 'package:comunic/utils/flutter_utils.dart';
import 'package:package_info/package_info.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:version/version.dart';
/// Application version helper
@ -7,15 +7,15 @@ import 'package:version/version.dart';
/// @author Pierre Hubert
class VersionHelper {
static PackageInfo _info;
static PackageInfo? _info;
static Future<void> ensureLoaded() async {
if (!isWeb) _info = await PackageInfo.fromPlatform();
}
/// Get current version information
static PackageInfo get info => _info;
static PackageInfo? get info => _info;
/// Get current application version, in parsed format
static Version get version => Version.parse(info.version);
static Version get version => Version.parse(info!.version);
}

View File

@ -1,5 +1,4 @@
import 'package:comunic/models/api_request.dart';
import 'package:flutter/material.dart';
/// Virtual directory helper
///
@ -9,12 +8,12 @@ enum VirtualDirectoryType { USER, GROUP, NONE }
class VirtualDirectoryResult {
final VirtualDirectoryType type;
final int id;
final int? id;
const VirtualDirectoryResult({
@required this.type,
required this.type,
this.id,
}) : assert(type != null);
});
}
class VirtualDirectoryHelper {
@ -43,7 +42,6 @@ class VirtualDirectoryHelper {
default:
throw Exception("Unsupported virtual directory kind: $kind");
}
break;
default:
throw new Exception("Could not get virtual directory!");

View File

@ -15,7 +15,7 @@ class WebAppHelper {
static Future<MembershipList> getMemberships() async {
final response =
(await APIRequest.withLogin("webApp/getMemberships").execWithThrow())
.getArray();
.getArray()!;
return MembershipList()
..addAll(response
@ -26,7 +26,7 @@ class WebAppHelper {
}
/// Turn an API entry into a membership entry
static Membership _apiToMembership(Map<String, dynamic> entry) {
static Membership? _apiToMembership(Map<String, dynamic> entry) {
switch (entry["type"]) {
case "conversation":
return Membership.conversation(

View File

@ -15,7 +15,7 @@ import 'package:web_socket_channel/web_socket_channel.dart';
/// @author Pierre Hubert
class WebSocketHelper {
static WebSocketChannel _ws;
static WebSocketChannel? _ws;
static int _counter = 0;
@ -23,11 +23,11 @@ class WebSocketHelper {
/// Check out whether we are currently connected to WebSocket or not
static bool isConnected() {
return _ws != null && _ws.closeCode == null;
return _ws != null && _ws!.closeCode == null;
}
/// Get WebSocket access token
static Future<String> _getWsToken() async =>
static Future<String?> _getWsToken() async =>
(await APIRequest(uri: "ws/token", needLogin: true).exec())
.assertOk()
.getObject()["token"];
@ -47,7 +47,7 @@ class WebSocketHelper {
// Connect
_ws = WebSocketChannel.connect(wsURI);
_ws.stream.listen(
_ws!.stream.listen(
// When we got data
(data) {
print("WS New data: $data");
@ -75,7 +75,7 @@ class WebSocketHelper {
/// Close current WebSocket (if any)
static close() {
if (isConnected()) _ws.sink.close();
if (isConnected()) _ws!.sink.close();
}
/// Send a new message
@ -93,7 +93,7 @@ class WebSocketHelper {
print("WS Send message: $msg");
_ws.sink.add(msg);
_ws!.sink.add(msg);
_requests[id] = completer;
return completer.future;
@ -240,11 +240,11 @@ class WebSocketHelper {
// Handles errors
if (msg.title != "success") {
completer.completeError(Exception("Could not process request!"));
completer!.completeError(Exception("Could not process request!"));
return;
}
completer.complete(msg.data);
completer!.complete(msg.data);
}
}

View File

@ -16,4 +16,7 @@ class AbstractList<E> extends ListBase<E> {
@override
void operator []=(int index, E value) => _list[index] = value;
@override
void add(E element) => _list.add(element);
}

View File

@ -11,7 +11,7 @@ class BaseSet<T> extends SetBase<T> {
bool add(T value) => _set.add(value);
@override
bool contains(Object element) => _set.contains(element);
bool contains(Object? element) => _set.contains(element);
@override
Iterator<T> get iterator => _set.iterator;
@ -20,10 +20,10 @@ class BaseSet<T> extends SetBase<T> {
int get length => _set.length;
@override
T lookup(Object element) => _set.lookup(element);
T? lookup(Object? element) => _set.lookup(element);
@override
bool remove(Object value) => _set.remove(value);
bool remove(Object? value) => _set.remove(value);
@override
Set<T> toSet() => _set.toSet();

View File

@ -10,10 +10,10 @@ class CallMembersList extends AbstractList<CallMember> {
Set<int> get usersID => this.map((f) => f.userID).toSet();
/// Remove a specific member from this list
void removeUser(int userID) => this.removeWhere((f) => f.userID == userID);
void removeUser(int? userID) => this.removeWhere((f) => f.userID == userID);
/// Get the connection of a specific user
CallMember getUser(int userID) => this.firstWhere((f) => f.userID == userID);
CallMember getUser(int? userID) => this.firstWhere((f) => f.userID == userID);
/// Extract ready peers from this list
CallMembersList get readyPeers =>

View File

@ -1,5 +1,4 @@
import 'dart:collection';
import 'package:comunic/lists/abstract_list.dart';
import 'package:comunic/models/comment.dart';
/// Comments list
@ -8,19 +7,7 @@ import 'package:comunic/models/comment.dart';
///
/// @author Pierre HUBERT
class CommentsList extends ListBase<Comment> {
List<Comment> _list = [];
int get length => _list.length;
set length(int l) => _list.length = l;
@override
Comment operator [](int index) => _list[index];
@override
void operator []=(int index, Comment value) => _list[index] = value;
class CommentsList extends AbstractList<Comment> {
/// Get the list of users in this comments, as a set
Set<int> get usersID => map((f) => f.userID).toSet();
}

View File

@ -1,31 +1,14 @@
import 'dart:collection';
import 'package:comunic/lists/abstract_list.dart';
import 'package:comunic/models/conversation_message.dart';
/// Conversations messages list
///
/// @author Pierre HUBERT
class ConversationMessagesList extends ListBase<ConversationMessage> {
final List<ConversationMessage> _list = [];
set length(int v) => _list.length = v;
int get length => _list.length;
@override
ConversationMessage operator [](int index) {
return _list[index];
}
@override
void operator []=(int index, ConversationMessage value) {
_list[index] = value;
}
class ConversationMessagesList extends AbstractList<ConversationMessage> {
/// Get the list of the users ID who own a message in this list
Set<int> getUsersID() {
final Set<int> users = Set();
Set<int?> getUsersID() {
final Set<int?> users = Set();
for (ConversationMessage message in this) users.addAll(message.usersID);
@ -33,18 +16,18 @@ class ConversationMessagesList extends ListBase<ConversationMessage> {
}
/// Get the ID of the last message present in this list
int get lastMessageID {
int lastMessageID = 0;
int? get lastMessageID {
int? lastMessageID = 0;
for (ConversationMessage message in this)
if (message.id > lastMessageID) lastMessageID = message.id;
if (message.id! > lastMessageID!) lastMessageID = message.id;
return lastMessageID;
}
/// Get the ID of the first message present in this list
int get firstMessageID {
int firstMessageID = this[0].id;
int? get firstMessageID {
int? firstMessageID = this[0].id;
for (ConversationMessage message in this)
if (message.id < firstMessageID) firstMessageID = message.id;
if (message.id! < firstMessageID!) firstMessageID = message.id;
return firstMessageID;
}
@ -55,5 +38,5 @@ class ConversationMessagesList extends ListBase<ConversationMessage> {
}
/// Remove a message from this list
void removeMsg(int id) => removeWhere((f) => f.id == id);
void removeMsg(int? id) => removeWhere((f) => f.id == id);
}

View File

@ -22,12 +22,15 @@ class ConversationsList extends ListBase<Conversation> {
/// Get the entire lists of users ID in this list
Set<int> get allUsersID {
final Set<int> list = Set();
forEach((c) => c.members.forEach((member) => list.add(member.userID)));
forEach((c) => c.members!.forEach((member) => list.add(member.userID)));
return list;
}
/// Get the entire lists of groups ID in this list
Set<int> get allGroupsID => where((element) => element.isGroupConversation)
Set<int?> get allGroupsID => where((element) => element.isGroupConversation)
.map((e) => e.groupID)
.toSet();
@override
void add(Conversation element) => _list.add(element);
}

View File

@ -7,8 +7,7 @@ import 'package:comunic/models/custom_emoji.dart';
class CustomEmojiesList extends AbstractList<CustomEmoji> {
/// Check if an emoji, identified by its shortcut, is present in this list
bool hasShortcut(String shortcut) =>
firstWhere((f) => f.shortcut == shortcut, orElse: () => null) != null;
bool hasShortcut(String shortcut) => any((f) => f.shortcut == shortcut);
/// Serialize this list
List<Map<String, dynamic>> toSerializableList() =>

View File

@ -7,7 +7,7 @@ import 'package:comunic/models/forez_presence.dart';
class PresenceSet extends BaseSet<Presence> {
/// Get the presence of a specific user
PresenceSet getForUser(int userID) =>
PresenceSet getForUser(int? userID) =>
PresenceSet()..addAll(where((element) => element.userID == userID));
bool containsDate(DateTime dt) => any(
@ -24,11 +24,11 @@ class PresenceSet extends BaseSet<Presence> {
element.day == dt.day,
);
void toggleDate(DateTime dt, int userID) {
void toggleDate(DateTime dt, int? userID) {
if (containsDate(dt))
removeDate(dt);
else
add(Presence.fromDateTime(dt, userID));
add(Presence.fromDateTime(dt, userID!));
}
int countAtDate(DateTime dt) => where(

View File

@ -1,24 +1,11 @@
import 'dart:collection';
import 'package:comunic/lists/abstract_list.dart';
import 'package:comunic/models/friend.dart';
/// List of friends of the user
///
/// @author Pierre HUBERT
class FriendsList extends ListBase<Friend> {
List<Friend> _list = [];
int get length => _list.length;
set length(int length) => _list.length = length;
@override
Friend operator [](int index) => _list[index];
@override
void operator []=(int index, Friend value) => _list[index] = value;
class FriendsList extends AbstractList<Friend> {
/// Get the ID of all the friends of the current user
List<int> get usersId => map((f) => f.id).toList();
}

View File

@ -6,23 +6,23 @@ import 'package:comunic/models/group.dart';
///
/// @author Pierre HUBERT
class GroupsList extends MapBase<int, Group> {
final Map<int, Group> _groups = Map();
class GroupsList extends MapBase<int?, Group> {
final Map<int?, Group?> _groups = Map();
@override
Group operator [](Object key) => _groups[key];
Group? operator [](Object? key) => _groups[key];
@override
void operator []=(int key, Group value) => _groups[key] = value;
void operator []=(int? key, Group? value) => _groups[key] = value;
@override
void clear() => _groups.clear();
@override
Iterable<int> get keys => _groups.keys;
Iterable<int?> get keys => _groups.keys;
@override
Group remove(Object key) => _groups.remove(key);
Group? remove(Object? key) => _groups.remove(key);
Group getGroup(int id) => this[id];
Group? getGroup(int? id) => this[id];
}

View File

@ -5,20 +5,20 @@ import 'package:comunic/models/membership.dart';
///
/// @author Pierre Hubert
class MembershipList extends AbstractList<Membership> {
class MembershipList extends AbstractList<Membership?> {
/// Get the IDs of all the users included in some way in this list
Set<int> get usersId {
final s = Set<int>();
Set<int?> get usersId {
final s = Set<int?>();
forEach((m) {
switch (m.type) {
switch (m!.type) {
case MembershipType.FRIEND:
s.add(m.friend.id);
s.add(m.friend!.id);
break;
case MembershipType.GROUP:
break;
case MembershipType.CONVERSATION:
s.addAll(m.conversation.membersID);
s.addAll(m.conversation!.membersID);
break;
}
});
@ -27,16 +27,16 @@ class MembershipList extends AbstractList<Membership> {
}
/// Get the ID of the groups included in this list
Set<int> get groupsId => where((f) => f.type == MembershipType.GROUP)
.map((f) => f.groupID)
Set<int?> get groupsId => where((f) => f!.type == MembershipType.GROUP)
.map((f) => f!.groupID)
.toSet();
/// Remove a friend membership from the list
void removeFriend(int friendID) => remove(firstWhere(
(f) => f.type == MembershipType.FRIEND && f.friend.id == friendID));
(f) => f!.type == MembershipType.FRIEND && f.friend!.id == friendID));
/// Get the list of conversations of a group
Set<Membership> getGroupConversations(int groupID) => where((element) =>
element.type == MembershipType.CONVERSATION &&
element.conversation.groupID == groupID).toSet();
Set<Membership?> getGroupConversations(int groupID) => where((element) =>
element!.type == MembershipType.CONVERSATION &&
element.conversation!.groupID == groupID).toSet();
}

View File

@ -8,8 +8,8 @@ import 'package:comunic/models/notification.dart';
class NotificationsList extends AbstractList<Notification> {
/// Get the ID of all the users related to the notifications
/// included in the list
Set<int> get usersIds {
final list = Set<int>();
Set<int?> get usersIds {
final list = Set<int?>();
forEach((n) {
list.add(n.fromUser);
@ -28,8 +28,8 @@ class NotificationsList extends AbstractList<Notification> {
/// Get the ID of all the groups related to the notifications
/// included in the list
Set<int> get groupsIds {
final list = Set<int>();
Set<int?> get groupsIds {
final list = Set<int?>();
forEach((n) {
if (n.onElemType == NotificationElementType.GROUP_PAGE)

View File

@ -1,5 +1,4 @@
import 'dart:collection';
import 'package:comunic/lists/abstract_list.dart';
import 'package:comunic/models/post.dart';
/// Posts List
@ -8,37 +7,25 @@ import 'package:comunic/models/post.dart';
///
/// @author Pierre HUBERT
class PostsList extends ListBase<Post> {
List<Post> _list = [];
int get length => _list.length;
set length(int l) => _list.length = l;
@override
Post operator [](int index) => _list[index];
@override
void operator []=(int index, Post value) => _list[index] = value;
class PostsList extends AbstractList<Post> {
// Get the list of users ID in this set
Set<int> get usersID {
Set<int> set = Set();
Set<int?> get usersID {
Set<int?> set = Set();
forEach((p) {
set.add(p.userID);
if (p.userPageID != null && p.userPageID > 0) set.add(p.userPageID);
if (p.userPageID != null && p.userPageID! > 0) set.add(p.userPageID);
if (p.comments != null) set.addAll(p.comments.usersID);
if (p.comments != null) set.addAll(p.comments!.usersID);
});
return set;
}
/// Get the list of groups in this list of posts
Set<int> get groupsID {
Set<int> set = Set();
Set<int?> get groupsID {
Set<int?> set = Set();
forEach((p) {
if (p.isGroupPost) set.add(p.groupID);
@ -49,8 +36,7 @@ class PostsList extends ListBase<Post> {
/// Get the ID of the oldest post of this list. Returns 0 if the list is empty
int get oldestID {
if(isEmpty)
return 0;
if (isEmpty) return 0;
return this.elementAt(length - 1).id;
}

View File

@ -7,8 +7,8 @@ import 'package:comunic/models/unread_conversation.dart';
class UnreadConversationsList extends AbstractList<UnreadConversation> {
/// Get the ID of the users included in this list
Set<int> get usersID {
final set = Set<int>();
Set<int?> get usersID {
final set = Set<int?>();
forEach((element) {
set.addAll(element.conv.membersID);
set.addAll(element.message.usersID);
@ -17,8 +17,8 @@ class UnreadConversationsList extends AbstractList<UnreadConversation> {
}
/// Get the ID of the groups references ind this list
Set<int> get groupsID {
final set = Set<int>();
Set<int?> get groupsID {
final set = Set<int?>();
forEach((element) {
if (element.conv.isGroupConversation) set.add(element.conv.groupID);
});

View File

@ -24,7 +24,7 @@ class UsersList extends ListBase<User> {
}
/// Find a user with a specific ID
User getUser(int userID) {
User getUser(int? userID) {
for (int i = 0; i < this.length; i++)
if (this[i].id == userID) return this[i];
@ -32,8 +32,11 @@ class UsersList extends ListBase<User> {
}
/// Check if the user is included in this list or not
bool hasUser(int userID) => any((f) => f.id == userID);
bool hasUser(int? userID) => any((f) => f.id == userID);
/// Get the list of users ID present in this list
List<int> get usersID => List.generate(length, (i) => this[i].id);
List<int?> get usersID => List.generate(length, (i) => this[i].id);
@override
void add(User element) => _list.add(element);
}

View File

@ -7,7 +7,7 @@ import 'package:comunic/models/config.dart';
import 'package:comunic/ui/widgets/init_widget.dart';
import 'package:comunic/utils/flutter_utils.dart';
import 'package:comunic/utils/intl_utils.dart';
import 'package:connectivity/connectivity.dart';
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
@ -44,10 +44,9 @@ class ComunicApplication extends StatefulWidget {
final PreferencesHelper preferences;
const ComunicApplication({
Key key,
@required this.preferences,
}) : assert(preferences != null),
super(key: key);
Key? key,
required this.preferences,
}) : super(key: key);
@override
ComunicApplicationState createState() => ComunicApplicationState();

View File

@ -11,7 +11,7 @@ import 'package:comunic/utils/flutter_utils.dart';
/// Fix HTTPS issue
class MyHttpOverride extends HttpOverrides {
@override
HttpClient createHttpClient(SecurityContext context) {
HttpClient createHttpClient(SecurityContext? context) {
return super.createHttpClient(context)
..badCertificateCallback = (cert, host, port) {
return host == "devweb.local"; // Forcefully trust local website

View File

@ -1,5 +1,3 @@
import 'package:flutter/widgets.dart';
/// Account image settings
///
/// @author Pierre Hubert
@ -12,10 +10,8 @@ class AccountImageSettings {
final AccountImageVisibilityLevels visibility;
const AccountImageSettings({
@required this.hasImage,
@required this.imageURL,
@required this.visibility,
}) : assert(hasImage != null),
assert(imageURL != null),
assert(visibility != null);
required this.hasImage,
required this.imageURL,
required this.visibility,
});
}

View File

@ -1,7 +1,6 @@
import 'package:comunic/enums/likes_type.dart';
import 'package:comunic/models/conversation.dart';
import 'package:comunic/models/like_element.dart';
import 'package:flutter/material.dart';
import 'group.dart';
@ -10,36 +9,35 @@ import 'group.dart';
/// @author Pierre Hubert
class AdvancedGroupInfo extends Group implements LikeElement {
bool isMembersListPublic;
final int timeCreate;
bool? isMembersListPublic;
final int? timeCreate;
String description;
String url;
int likes;
bool userLike;
List<Conversation> conversations;
List<Conversation>? conversations;
bool isForezGroup;
AdvancedGroupInfo({
@required int id,
@required String name,
@required String iconURL,
@required int numberMembers,
@required GroupMembershipLevel membershipLevel,
@required GroupVisibilityLevel visibilityLevel,
@required GroupRegistrationLevel registrationLevel,
@required GroupPostCreationLevel postCreationLevel,
@required String virtualDirectory,
@required bool following,
@required this.isMembersListPublic,
@required this.timeCreate,
@required this.description,
@required this.url,
@required this.likes,
@required this.userLike,
@required this.conversations,
@required this.isForezGroup,
}) : assert(isForezGroup != null),
super(
required int id,
required String name,
required String iconURL,
required int numberMembers,
required GroupMembershipLevel membershipLevel,
required GroupVisibilityLevel visibilityLevel,
required GroupRegistrationLevel registrationLevel,
required GroupPostCreationLevel postCreationLevel,
required String virtualDirectory,
required bool following,
required this.isMembersListPublic,
required this.timeCreate,
required this.description,
required this.url,
required this.likes,
required this.userLike,
required this.conversations,
required this.isForezGroup,
}) : super(
id: id,
name: name,
iconURL: iconURL,
@ -54,8 +52,7 @@ class AdvancedGroupInfo extends Group implements LikeElement {
@override
LikesType likeType = LikesType.GROUP;
get hasURL => url != null && url.isNotEmpty && url != "null";
get hasURL => url.isNotEmpty && url != "null";
get hasDescription =>
description != null && description.isNotEmpty && description != "null";
get hasDescription => description.isNotEmpty && description != "null";
}

View File

@ -3,51 +3,42 @@ import 'package:comunic/enums/user_page_visibility.dart';
import 'package:comunic/lists/custom_emojies_list.dart';
import 'package:comunic/models/like_element.dart';
import 'package:comunic/models/user.dart';
import 'package:meta/meta.dart';
/// Advanced user information
///
/// @author Pierre HUBERT
class AdvancedUserInfo extends User implements LikeElement {
final String emailAddress;
final String? emailAddress;
final String publicNote;
final bool canPostTexts;
final bool isFriendsListPublic;
final int numberFriends;
final int accountCreationTime;
final String personalWebsite;
final String location;
final String? location;
bool userLike;
int likes;
AdvancedUserInfo({
@required int id,
@required String firstName,
@required String lastName,
@required UserPageVisibility pageVisibility,
@required String virtualDirectory,
@required String accountImageURL,
@required CustomEmojiesList customEmojies,
@required this.emailAddress,
@required this.publicNote,
@required this.canPostTexts,
@required this.isFriendsListPublic,
@required this.numberFriends,
@required this.accountCreationTime,
@required this.personalWebsite,
@required this.location,
@required this.userLike,
@required this.likes,
}) : assert(publicNote != null),
assert(canPostTexts != null),
assert(isFriendsListPublic != null),
assert(numberFriends != null),
assert(accountCreationTime != null),
assert(personalWebsite != null),
assert(userLike != null),
assert(likes != null),
super(
required int id,
required String firstName,
required String lastName,
required UserPageVisibility pageVisibility,
required String? virtualDirectory,
required String accountImageURL,
required CustomEmojiesList customEmojies,
required this.emailAddress,
required this.publicNote,
required this.canPostTexts,
required this.isFriendsListPublic,
required this.numberFriends,
required this.accountCreationTime,
required this.personalWebsite,
required this.location,
required this.userLike,
required this.likes,
}) : super(
id: id,
firstName: firstName,
lastName: lastName,
@ -60,9 +51,9 @@ class AdvancedUserInfo extends User implements LikeElement {
bool get hasPersonalWebsite => personalWebsite.isNotEmpty;
bool get hasEmailAddress => emailAddress != null && emailAddress.isNotEmpty;
bool get hasEmailAddress => emailAddress != null && emailAddress!.isNotEmpty;
bool get hasLocation => location != null && location.isNotEmpty;
bool get hasLocation => location != null && location!.isNotEmpty;
@override
LikesType get likeType => LikesType.USER;

View File

@ -4,7 +4,6 @@ import 'package:comunic/helpers/api_helper.dart';
import 'package:comunic/models/api_response.dart';
import 'package:dio/dio.dart';
import 'package:http_parser/http_parser.dart';
import 'package:meta/meta.dart';
/// API Request model
///
@ -14,8 +13,8 @@ import 'package:meta/meta.dart';
class BytesFile {
final String filename;
final List<int> bytes;
final MediaType type;
final List<int>? bytes;
final MediaType? type;
const BytesFile(
this.filename,
@ -27,42 +26,36 @@ class BytesFile {
class APIRequest {
final String uri;
final bool needLogin;
ProgressCallback progressCallback;
CancelToken cancelToken;
Map<String, String> args;
ProgressCallback? progressCallback;
CancelToken? cancelToken;
Map<String, String?>? args;
Map<String, File> files = Map();
Map<String, BytesFile> bytesFiles = Map();
Map<String, BytesFile?> bytesFiles = Map();
APIRequest({@required this.uri, this.needLogin = false, this.args})
: assert(uri != null),
assert(needLogin != null) {
APIRequest({required this.uri, this.needLogin = false, this.args}) {
if (this.args == null) this.args = Map();
}
APIRequest.withLogin(this.uri, {this.args})
: needLogin = true,
assert(uri != null) {
APIRequest.withLogin(this.uri, {this.args}) : needLogin = true {
if (args == null) this.args = Map();
}
APIRequest.withoutLogin(this.uri, {this.args})
: needLogin = false,
assert(uri != null) {
APIRequest.withoutLogin(this.uri, {this.args}) : needLogin = false {
if (args == null) this.args = Map();
}
APIRequest addString(String name, String value) {
args[name] = value;
APIRequest addString(String name, String? value) {
args![name] = value;
return this;
}
APIRequest addInt(String name, int value) {
args[name] = value.toString();
APIRequest addInt(String name, int? value) {
args![name] = value.toString();
return this;
}
APIRequest addBool(String name, bool value) {
args[name] = value ? "true" : "false";
args![name] = value ? "true" : "false";
return this;
}
@ -71,12 +64,12 @@ class APIRequest {
return this;
}
APIRequest addBytesFile(String name, BytesFile file) {
APIRequest addBytesFile(String name, BytesFile? file) {
this.bytesFiles[name] = file;
return this;
}
void addArgs(Map<String, String> newArgs) => args.addAll(newArgs);
void addArgs(Map<String, String> newArgs) => args!.addAll(newArgs);
/// Execute the request
Future<APIResponse> exec() async => APIHelper().exec(this);

View File

@ -6,13 +6,13 @@ import 'dart:convert';
class APIResponse {
final int code;
final String content;
final String? content;
const APIResponse(this.code, this.content) : assert(code != null);
const APIResponse(this.code, this.content);
List<dynamic> getArray() => jsonDecode(this.content);
List<dynamic>? getArray() => jsonDecode(this.content!);
Map<String, dynamic> getObject() => jsonDecode(this.content);
Map<String, dynamic> getObject() => jsonDecode(this.content!);
/// Check if the request is successful or not
bool get isOK => code == 200;

View File

@ -1,4 +1,4 @@
import 'package:flutter/cupertino.dart';
/// Application settings
///
@ -10,10 +10,8 @@ class ApplicationPreferences {
bool showPerformancesOverlay;
ApplicationPreferences({
@required this.enableDarkMode,
@required this.forceMobileMode,
@required this.showPerformancesOverlay,
}) : assert(enableDarkMode != null),
assert(forceMobileMode != null),
assert(showPerformancesOverlay != null);
required this.enableDarkMode,
required this.forceMobileMode,
required this.showPerformancesOverlay,
}) ;
}

View File

@ -1,4 +1,4 @@
import 'package:meta/meta.dart';
/// Authentication details
///
@ -8,7 +8,5 @@ class AuthenticationDetails {
final String email;
final String password;
const AuthenticationDetails({@required this.email, @required this.password})
: assert(email != null),
assert(password != null);
const AuthenticationDetails({required this.email, required this.password});
}

View File

@ -1,5 +1,4 @@
import 'package:comunic/helpers/database/database_contract.dart';
import 'package:meta/meta.dart';
/// Cache base model
///
@ -8,7 +7,7 @@ import 'package:meta/meta.dart';
abstract class CacheModel {
final int id;
const CacheModel({@required this.id}) : assert(id != null);
const CacheModel({required this.id});
/// Initialize a CacheModel from a map
CacheModel.fromMap(Map<String, dynamic> map)

View File

@ -1,5 +1,3 @@
import 'package:flutter/material.dart';
/// Call configuration
///
/// @author Pierre Hubert
@ -8,8 +6,8 @@ class CallConfig {
final List<String> iceServers;
const CallConfig({
@required this.iceServers,
}) : assert(iceServers != null);
required this.iceServers,
});
/// Turn this call configuration into the right for the WebRTC plugin
Map<String, dynamic> get pluginConfig => {

View File

@ -1,4 +1,3 @@
import 'package:flutter/material.dart';
import 'package:flutter_webrtc/flutter_webrtc.dart';
/// Single call member information
@ -10,14 +9,13 @@ enum MemberStatus { JOINED, READY }
class CallMember {
final int userID;
MemberStatus status;
MediaStream stream;
MediaStream? stream;
CallMember({
@required this.userID,
required this.userID,
this.status = MemberStatus.JOINED,
}) : assert(userID != null),
assert(status != null);
});
bool get hasVideoStream =>
stream != null && stream.getVideoTracks().length > 0;
stream != null && stream!.getVideoTracks().length > 0;
}

View File

@ -2,7 +2,6 @@ import 'package:comunic/enums/likes_type.dart';
import 'package:comunic/models/displayed_content.dart';
import 'package:comunic/models/like_element.dart';
import 'package:comunic/utils/account_utils.dart' as account;
import 'package:meta/meta.dart';
/// Comments
///
@ -16,29 +15,22 @@ class Comment implements LikeElement {
final int postID;
final int timeSent;
DisplayedString content;
final String imageURL;
final String? imageURL;
int likes;
bool userLike;
Comment({
@required this.id,
@required this.userID,
@required this.postID,
@required this.timeSent,
@required this.content,
@required this.imageURL,
@required this.likes,
@required this.userLike,
}) : assert(id != null),
assert(userID != null),
assert(postID != null),
assert(timeSent != null),
assert(content != null),
assert(likes != null),
assert(userLike != null);
required this.id,
required this.userID,
required this.postID,
required this.timeSent,
required this.content,
required this.imageURL,
required this.likes,
required this.userLike,
});
bool get hasContent =>
content != null && !content.isNull && content.length > 0;
bool get hasContent => !content.isNull && content.length > 0;
bool get hasImage => imageURL != null;

View File

@ -16,27 +16,27 @@ class Config {
// Theme customization
final Color splashBackgroundColor;
final Color primaryColor;
final Color primaryColorDark;
final Color? primaryColor;
final Color? primaryColorDark;
final String appName;
final String appQuickDescription;
final Color unreadConversationColor;
final Color defaultConversationColor;
final String? appQuickDescription;
final Color? unreadConversationColor;
final Color? defaultConversationColor;
// Entries for the welcome tour
final TourEntriesBuilder toursEntriesBuilder;
final TourEntriesBuilder? toursEntriesBuilder;
// Custom initialization
final Future<void> Function() additionalLoading;
final Future<void> Function()? additionalLoading;
// Custom main application route
final Widget Function(BuildContext, GlobalKey) mainRouteBuilder;
final Widget Function(BuildContext, GlobalKey)? mainRouteBuilder;
const Config({
@required this.apiServerName,
@required this.apiServerUri,
@required this.apiServerSecure,
@required this.clientName,
required this.apiServerName,
required this.apiServerUri,
required this.apiServerSecure,
required this.clientName,
this.splashBackgroundColor = defaultColor,
this.primaryColor,
this.primaryColorDark,
@ -47,17 +47,12 @@ class Config {
this.toursEntriesBuilder,
this.additionalLoading,
this.mainRouteBuilder,
}) : assert(apiServerName != null),
assert(apiServerUri != null),
assert(apiServerSecure != null),
assert(clientName != null),
assert(splashBackgroundColor != null),
assert(appName != null);
});
/// Get and set static configuration
static Config _config;
static Config? _config;
static Config get() {
static Config? get() {
return _config;
}
@ -68,5 +63,5 @@ class Config {
/// Get the current configuration of the application
Config config() {
return Config.get();
return Config.get()!;
}

View File

@ -1,3 +1,4 @@
import 'package:collection/collection.dart' show IterableExtension;
import 'package:comunic/helpers/serialization/base_serialization_helper.dart';
import 'package:comunic/models/conversation_member.dart';
import 'package:comunic/utils/account_utils.dart';
@ -12,44 +13,38 @@ import 'group.dart';
enum CallCapabilities { NONE, AUDIO, VIDEO }
class Conversation extends SerializableElement<Conversation> {
final int id;
final int lastActivity;
final String name;
final Color color;
final String logoURL;
final int groupID;
final GroupMembershipLevel groupMinMembershipLevel;
final List<ConversationMember> members;
final bool canEveryoneAddMembers;
final int? id;
final int? lastActivity;
final String? name;
final Color? color;
final String? logoURL;
final int? groupID;
final GroupMembershipLevel? groupMinMembershipLevel;
final List<ConversationMember>? members;
final bool? canEveryoneAddMembers;
final CallCapabilities callCapabilities;
final bool isHavingCall;
Conversation({
@required this.id,
@required this.lastActivity,
@required this.name,
@required this.color,
@required this.logoURL,
@required this.groupID,
@required this.groupMinMembershipLevel,
@required this.members,
@required this.canEveryoneAddMembers,
/*required*/ required int this.id,
/*required*/ required int this.lastActivity,
required this.name,
required this.color,
required this.logoURL,
required this.groupID,
required this.groupMinMembershipLevel,
/*required*/ required List<ConversationMember> this.members,
/*required*/ required bool this.canEveryoneAddMembers,
this.callCapabilities = CallCapabilities.NONE,
this.isHavingCall = false,
}) : assert(id != null),
assert(lastActivity != null),
assert(members != null),
assert(canEveryoneAddMembers != null),
assert((groupID == null) == (groupMinMembershipLevel == null)),
assert(callCapabilities != null),
assert(isHavingCall != null);
}) : assert((groupID == null) == (groupMinMembershipLevel == null));
/// Check out whether a conversation has a fixed name or not
bool get hasName => this.name != null;
/// Get current user membership
ConversationMember get membership =>
members.firstWhere((m) => m.userID == userID());
members!.firstWhere((m) => m.userID == userID());
/// Check out whether current user of the application is an admin
bool get isAdmin => membership.isAdmin;
@ -61,17 +56,17 @@ class Conversation extends SerializableElement<Conversation> {
bool get following => membership.following;
/// Get the list of members in the conversation
Set<int> get membersID => members.map((e) => e.userID).toSet();
Set<int?> get membersID => members!.map((e) => e.userID).toSet();
/// Get the list of admins in the conversation
Set<int> get adminsID =>
members.where((e) => e.isAdmin).map((e) => e.userID).toSet();
Set<int?> get adminsID =>
members!.where((e) => e.isAdmin).map((e) => e.userID).toSet();
/// Get the list of the OTHER members of the conversation (all except current user)
Set<int> get otherMembersID => membersID..remove(userID());
Set<int?> get otherMembersID => membersID..remove(userID());
/// Check if the last message has been seen or not
bool get sawLastMessage => lastActivity <= membership.lastAccessTime;
bool get sawLastMessage => lastActivity! <= membership.lastAccessTime;
/// Check out whether a conversation is managed or not
bool get isManaged => isGroupConversation;
@ -86,9 +81,8 @@ class Conversation extends SerializableElement<Conversation> {
color = map["color"] == null ? null : Color(map["color"]),
logoURL = map["logoURL"],
groupID = map["groupID"],
groupMinMembershipLevel = GroupMembershipLevel.values.firstWhere(
(element) => element.toString() == map["groupMinMembershipLevel"],
orElse: () => null),
groupMinMembershipLevel = GroupMembershipLevel.values.firstWhereOrNull(
(element) => element.toString() == map["groupMinMembershipLevel"]),
lastActivity = map["lastActivity"],
members = map["members"]
.map((el) => ConversationMember.fromJSON(el))
@ -109,13 +103,13 @@ class Conversation extends SerializableElement<Conversation> {
"groupID": groupID,
"groupMinMembershipLevel": groupMinMembershipLevel?.toString(),
"lastActivity": lastActivity,
"members": members.map((e) => e.toJson()).toList(),
"members": members!.map((e) => e.toJson()).toList(),
"canEveryoneAddMembers": canEveryoneAddMembers,
};
}
@override
int compareTo(Conversation other) {
return other.lastActivity.compareTo(this.lastActivity);
return other.lastActivity!.compareTo(this.lastActivity!);
}
}

View File

@ -1,5 +1,3 @@
import 'package:flutter/widgets.dart';
/// Conversation member
///
/// @author Pierre Hubert
@ -12,16 +10,12 @@ class ConversationMember {
final bool isAdmin;
const ConversationMember({
@required this.userID,
@required this.lastMessageSeen,
@required this.lastAccessTime,
@required this.following,
@required this.isAdmin,
}) : assert(userID != null),
assert(lastMessageSeen != null),
assert(lastAccessTime != null),
assert(following != null),
assert(isAdmin != null);
required this.userID,
required this.lastMessageSeen,
required this.lastAccessTime,
required this.following,
required this.isAdmin,
});
Map<String, dynamic> toJson() => {
'userID': userID,

View File

@ -30,27 +30,24 @@ const _ConversationFileMimeTypeMapping = {
};
class ConversationMessageFile {
final String url;
final int size;
final String name;
final String thumbnail;
final String type;
final String? url;
final int? size;
final String? name;
final String? thumbnail;
final String? type;
const ConversationMessageFile({
@required this.url,
@required this.size,
@required this.name,
@required this.thumbnail,
@required this.type,
}) : assert(url != null),
assert(size != null),
assert(name != null),
assert(type != null);
required String this.url,
required int this.size,
required String this.name,
required this.thumbnail,
required String this.type,
});
/// Get the type of file
ConversationMessageFileType get fileType {
ConversationMessageFileType? get fileType {
if (type != null && _ConversationFileMimeTypeMapping.containsKey(type))
return _ConversationFileMimeTypeMapping[type];
return _ConversationFileMimeTypeMapping[type!];
else
return ConversationMessageFileType.OTHER;
}
@ -102,21 +99,20 @@ enum ConversationServerMessageType {
class ConversationServerMessage {
final ConversationServerMessageType type;
final int userID;
final int userWhoAdded;
final int userAdded;
final int userWhoRemoved;
final int userRemoved;
final int? userID;
final int? userWhoAdded;
final int? userAdded;
final int? userWhoRemoved;
final int? userRemoved;
const ConversationServerMessage({
@required this.type,
@required this.userID,
@required this.userWhoAdded,
@required this.userAdded,
@required this.userWhoRemoved,
@required this.userRemoved,
}) : assert(type != null),
assert(userID != null ||
required this.type,
required this.userID,
required this.userWhoAdded,
required this.userAdded,
required this.userWhoRemoved,
required this.userRemoved,
}) : assert(userID != null ||
(type != ConversationServerMessageType.USER_CREATED_CONVERSATION &&
type != ConversationServerMessageType.USER_LEFT_CONV)),
assert((userWhoAdded != null && userAdded != null) ||
@ -124,7 +120,7 @@ class ConversationServerMessage {
assert((userWhoRemoved != null && userRemoved != null) ||
type != ConversationServerMessageType.USER_REMOVED_ANOTHER_USER);
Set<int> get usersID {
Set<int?> get usersID {
switch (type) {
case ConversationServerMessageType.USER_CREATED_CONVERSATION:
case ConversationServerMessageType.USER_LEFT_CONV:
@ -140,35 +136,31 @@ class ConversationServerMessage {
..add(userWhoRemoved)
..add(userRemoved);
}
throw Exception("Unsupported server message type!");
}
String getText(UsersList list) {
String? getText(UsersList? list) {
switch (type) {
case ConversationServerMessageType.USER_CREATED_CONVERSATION:
return tr("%1% created the conversation",
args: {"1": list.getUser(userID).fullName});
args: {"1": list!.getUser(userID).fullName});
case ConversationServerMessageType.USER_ADDED_ANOTHER_USER:
return tr("%1% added %2% to the conversation", args: {
"1": list.getUser(userWhoAdded).fullName,
"1": list!.getUser(userWhoAdded).fullName,
"2": list.getUser(userAdded).fullName,
});
case ConversationServerMessageType.USER_LEFT_CONV:
return tr("%1% left the conversation", args: {
"1": list.getUser(userID).fullName,
"1": list!.getUser(userID).fullName,
});
case ConversationServerMessageType.USER_REMOVED_ANOTHER_USER:
return tr("%1% removed %2% from the conversation", args: {
"1": list.getUser(userWhoRemoved).fullName,
"1": list!.getUser(userWhoRemoved).fullName,
"2": list.getUser(userRemoved).fullName,
});
}
throw Exception("Unsupported message type!");
}
Map<String, dynamic> toJson() => {
@ -191,29 +183,26 @@ class ConversationServerMessage {
}
class ConversationMessage extends SerializableElement<ConversationMessage> {
final int id;
final int convID;
final int userID;
final int timeSent;
final int? id;
final int? convID;
final int? userID;
final int? timeSent;
final DisplayedString message;
final ConversationMessageFile file;
final ConversationServerMessage serverMessage;
final ConversationMessageFile? file;
final ConversationServerMessage? serverMessage;
ConversationMessage({
@required this.id,
@required this.convID,
@required this.userID,
@required this.timeSent,
@required this.message,
@required this.file,
@required this.serverMessage,
}) : assert(id != null),
assert(convID != null),
assert(userID != null || serverMessage != null),
assert(timeSent != null),
assert(message != null || file != null || serverMessage != null);
required int this.id,
required int this.convID,
required this.userID,
required int this.timeSent,
required this.message,
required this.file,
required this.serverMessage,
}) : assert(userID != null || serverMessage != null),
assert(!message.isNull || file != null || serverMessage != null);
DateTime get date => DateTime.fromMillisecondsSinceEpoch(timeSent * 1000);
DateTime get date => DateTime.fromMillisecondsSinceEpoch(timeSent! * 1000);
bool get hasMessage => !message.isNull && message.length > 0;
@ -224,16 +213,16 @@ class ConversationMessage extends SerializableElement<ConversationMessage> {
bool get isServerMessage => serverMessage != null;
/// Get the list of the ID of the users implied in this message
Set<int> get usersID {
Set<int?> get usersID {
if (userID != null) return Set()..add(userID);
if (serverMessage != null) return serverMessage.usersID;
if (serverMessage != null) return serverMessage!.usersID;
return Set();
}
@override
int compareTo(ConversationMessage other) {
return id.compareTo(other.id);
return id!.compareTo(other.id!);
}
Map<String, dynamic> toJson() {

View File

@ -3,12 +3,11 @@
/// @author Pierre Hubert
class CountUnreadNotifications {
int notifications;
int conversations;
int? notifications;
int? conversations;
CountUnreadNotifications({
this.notifications,
this.conversations,
}) : assert(notifications != null),
assert(conversations != null);
required int this.notifications,
required int this.conversations,
});
}

View File

@ -1,24 +1,19 @@
import 'package:flutter/material.dart';
/// Single custom emoji information
///
/// @author Pierre Hubert
class CustomEmoji {
final int id;
final int userID;
final String shortcut;
final String url;
final int? id;
final int? userID;
final String? shortcut;
final String? url;
const CustomEmoji({
@required this.id,
@required this.userID,
@required this.shortcut,
@required this.url,
}) : assert(id != null),
assert(userID != null),
assert(shortcut != null),
assert(url != null);
required int this.id,
required int this.userID,
required String this.shortcut,
required String this.url,
});
Map<String, dynamic> toMap() => {
"id": id,

View File

@ -3,12 +3,12 @@
/// @author Pierre Hubert
class DataConservationPolicySettings {
int inactiveAccountLifeTime;
int notificationLifetime;
int commentsLifetime;
int postsLifetime;
int conversationMessagesLifetime;
int likesLifetime;
int? inactiveAccountLifeTime;
int? notificationLifetime;
int? commentsLifetime;
int? postsLifetime;
int? conversationMessagesLifetime;
int? likesLifetime;
DataConservationPolicySettings({
this.inactiveAccountLifeTime,

View File

@ -5,32 +5,32 @@ import 'package:comunic/utils/ui_utils.dart';
/// @author Pierre Hubert
class DisplayedString {
String _string;
String _parseCache;
String? _string;
String? _parseCache;
DisplayedString(this._string);
int get length => _string.length;
int get length => _string!.length;
bool get isEmpty => _string.isEmpty;
bool get isEmpty => _string!.isEmpty;
bool get isNull => _string == null;
String get content => _string;
String? get content => _string;
set content(String content) {
set content(String? content) {
_string = content;
_parseCache = null;
}
@override
String toString() {
return _string;
return _string!;
}
String get parsedString {
String? get parsedString {
if (_parseCache == null) {
_parseCache = parseEmojies(this._string);
_parseCache = parseEmojies(this._string!);
}
return _parseCache;

View File

@ -1,5 +1,3 @@
import 'package:flutter/material.dart';
/// Single presence information
///
/// @author Pierre Hubert
@ -11,19 +9,14 @@ class Presence {
final int day;
const Presence({
@required this.userID,
@required this.year,
@required this.month,
@required this.day,
}) : assert(userID != null),
assert(year != null),
assert(month != null),
assert(day != null);
required this.userID,
required this.year,
required this.month,
required this.day,
});
Presence.fromDateTime(DateTime dt, this.userID)
: assert(dt != null),
year = dt.year,
: year = dt.year,
month = dt.month,
day = dt.day,
assert(userID != null);
day = dt.day;
}

View File

@ -1,7 +1,6 @@
import 'package:comunic/helpers/database/database_contract.dart';
import 'package:comunic/models/cache_model.dart';
import 'package:comunic/utils/date_utils.dart';
import 'package:meta/meta.dart';
/// Single user Friend information
///
@ -9,28 +8,23 @@ import 'package:meta/meta.dart';
class Friend extends CacheModel implements Comparable<Friend> {
bool accepted;
final int lastActive;
final int? lastActive;
final bool following;
final bool canPostTexts;
Friend({
@required int id,
@required this.accepted,
@required this.lastActive,
@required this.following,
@required this.canPostTexts,
}) : assert(id != null),
assert(accepted != null),
assert(lastActive != null),
assert(following != null),
assert(canPostTexts != null),
super(id: id);
required int id,
required this.accepted,
required int this.lastActive,
required this.following,
required this.canPostTexts,
}) : super(id: id);
/// Check out whether friend is connected or not
bool get isConnected => time() - 30 < lastActive;
bool get isConnected => time() - 30 < lastActive!;
@override
int compareTo(Friend other) => other.lastActive.compareTo(lastActive);
int compareTo(Friend other) => other.lastActive!.compareTo(lastActive!);
@override
Map<String, dynamic> toMap() => {
@ -42,8 +36,7 @@ class Friend extends CacheModel implements Comparable<Friend> {
};
Friend.fromMap(Map<String, dynamic> map)
: assert(map != null),
accepted = map[FriendsListTableContract.C_ACCEPTED] == 1,
: accepted = map[FriendsListTableContract.C_ACCEPTED] == 1,
lastActive = map[FriendsListTableContract.C_LAST_ACTIVE],
following = map[FriendsListTableContract.C_FOLLOWING] == 1,
canPostTexts = map[FriendsListTableContract.C_CAN_POST_TEXTS] == 1,

View File

@ -1,5 +1,3 @@
import 'package:meta/meta.dart';
/// Simple friendship status
///
/// @author Pierre HUBERT
@ -12,16 +10,12 @@ class FriendStatus {
final bool following;
const FriendStatus({
@required this.userID,
@required this.areFriend,
@required this.sentRequest,
@required this.receivedRequest,
@required this.following,
}) : assert(userID != null),
assert(areFriend != null),
assert(sentRequest != null),
assert(receivedRequest != null),
assert(following != null);
required this.userID,
required this.areFriend,
required this.sentRequest,
required this.receivedRequest,
required this.following,
});
bool get noRequestExchanged => !areFriend && !sentRequest && !receivedRequest;
}

View File

@ -1,5 +1,4 @@
import 'package:comunic/enums/user_page_visibility.dart';
import 'package:flutter/material.dart';
/// General settings
///
@ -18,32 +17,21 @@ class GeneralSettings {
String virtualDirectory;
String personalWebsite;
String publicNote;
String location;
String? location;
GeneralSettings({
@required this.email,
@required this.firstName,
@required this.lastName,
@required this.pageVisibility,
@required this.allowComments,
@required this.allowPostsFromFriends,
@required this.allowComunicEmails,
@required this.publicFriendsList,
@required this.publicEmail,
@required this.virtualDirectory,
@required this.personalWebsite,
@required this.publicNote,
@required this.location,
}) : assert(email != null),
assert(firstName != null),
assert(lastName != null),
assert(pageVisibility != null),
assert(allowComments != null),
assert(allowPostsFromFriends != null),
assert(allowComunicEmails != null),
assert(publicFriendsList != null),
assert(publicEmail != null),
assert(virtualDirectory != null),
assert(personalWebsite != null),
assert(publicNote != null);
required this.email,
required this.firstName,
required this.lastName,
required this.pageVisibility,
required this.allowComments,
required this.allowPostsFromFriends,
required this.allowComunicEmails,
required this.publicFriendsList,
required this.publicEmail,
required this.virtualDirectory,
required this.personalWebsite,
required this.publicNote,
required this.location,
});
}

View File

@ -1,5 +1,4 @@
import 'package:comunic/utils/intl_utils.dart';
import 'package:meta/meta.dart';
/// Group information
///
@ -17,19 +16,18 @@ enum GroupMembershipLevel {
String membershipToText(GroupMembershipLevel level) {
switch (level) {
case GroupMembershipLevel.ADMINISTRATOR:
return tr("Administrator");
return tr("Administrator")!;
case GroupMembershipLevel.MODERATOR:
return tr("Moderator");
return tr("Moderator")!;
case GroupMembershipLevel.MEMBER:
return tr("Member");
return tr("Member")!;
case GroupMembershipLevel.INVITED:
return tr("Invited");
return tr("Invited")!;
case GroupMembershipLevel.PENDING:
return tr("Requested");
return tr("Requested")!;
case GroupMembershipLevel.VISITOR:
return tr("Visitor");
return tr("Visitor")!;
}
throw new Exception("Unreachable statement!");
}
enum GroupVisibilityLevel { OPEN, PRIVATE, SECRETE }
@ -51,25 +49,17 @@ class Group implements Comparable<Group> {
bool following;
Group({
@required this.id,
@required this.name,
@required this.iconURL,
@required this.numberMembers,
@required this.membershipLevel,
@required this.visibilityLevel,
@required this.registrationLevel,
@required this.postCreationLevel,
@required this.virtualDirectory,
@required this.following,
}) : assert(id != null),
assert(name != null),
assert(iconURL != null),
assert(numberMembers != null),
assert(membershipLevel != null),
assert(visibilityLevel != null),
assert(registrationLevel != null),
assert(postCreationLevel != null),
assert(following != null);
required this.id,
required this.name,
required this.iconURL,
required this.numberMembers,
required this.membershipLevel,
required this.visibilityLevel,
required this.registrationLevel,
required this.postCreationLevel,
required this.virtualDirectory,
required this.following,
});
get displayName => this.name;

View File

@ -1,5 +1,4 @@
import 'package:comunic/models/group.dart';
import 'package:flutter/material.dart';
/// Group membership information
///
@ -12,14 +11,11 @@ class GroupMembership {
final GroupMembershipLevel level;
const GroupMembership({
@required this.userID,
@required this.groupID,
@required this.timeCreate,
@required this.level,
}) : assert(userID != null),
assert(groupID != null),
assert(timeCreate != null),
assert(level != null);
required this.userID,
required this.groupID,
required this.timeCreate,
required this.level,
});
String get membershipText => membershipToText(level);

View File

@ -9,6 +9,6 @@ abstract class LikeElement {
int get id;
bool userLike;
int likes;
late bool userLike;
late int likes;
}

View File

@ -1,6 +1,5 @@
import 'package:comunic/models/conversation.dart';
import 'package:comunic/models/friend.dart';
import 'package:flutter/material.dart';
/// Membership information
///
@ -10,40 +9,37 @@ enum MembershipType { FRIEND, GROUP, CONVERSATION }
class Membership {
final MembershipType type;
final Conversation conversation;
final Friend friend;
final int groupID;
final int groupLastActive;
final Conversation? conversation;
final Friend? friend;
final int? groupID;
final int? groupLastActive;
Membership.conversation(this.conversation)
Membership.conversation(Conversation this.conversation)
: type = MembershipType.CONVERSATION,
friend = null,
groupID = null,
groupLastActive = null,
assert(conversation != null);
groupLastActive = null;
Membership.friend(this.friend)
Membership.friend(Friend this.friend)
: type = MembershipType.FRIEND,
conversation = null,
groupID = null,
groupLastActive = null,
assert(friend != null);
groupLastActive = null;
Membership.group({@required this.groupID, @required this.groupLastActive})
Membership.group(
{required int this.groupID, required int this.groupLastActive})
: type = MembershipType.GROUP,
conversation = null,
friend = null,
assert(groupID != null),
assert(groupLastActive != null);
friend = null;
int get lastActive {
int? get lastActive {
switch (type) {
case MembershipType.FRIEND:
return friend.lastActive;
return friend!.lastActive;
case MembershipType.GROUP:
return groupLastActive;
case MembershipType.CONVERSATION:
return conversation.lastActivity;
return conversation!.lastActivity;
default:
throw Exception("Unreachable statment!");
}

View File

@ -1,5 +1,3 @@
import 'package:flutter/widgets.dart';
/// New account information container
///
/// @author Pierre HUBERT
@ -11,12 +9,9 @@ class NewAccount {
final String password;
NewAccount({
@required this.firstName,
@required this.lastName,
@required this.email,
@required this.password,
}) : assert(firstName != null),
assert(lastName != null),
assert(email != null),
assert(password != null);
required this.firstName,
required this.lastName,
required this.email,
required this.password,
});
}

View File

@ -1,5 +1,3 @@
import 'package:meta/meta.dart';
import 'api_request.dart';
/// New comment information
@ -9,15 +7,15 @@ import 'api_request.dart';
class NewComment {
final int postID;
final String content;
final BytesFile image;
final BytesFile? image;
const NewComment({
@required this.postID,
@required this.content,
@required this.image,
}) : assert(postID != null);
required this.postID,
required this.content,
required this.image,
});
bool get hasContent => content != null && content.length > 0;
bool get hasContent => content.length > 0;
bool get hasImage => image != null;
}

View File

@ -6,19 +6,17 @@ import 'package:flutter/cupertino.dart';
class NewConversation {
final String name;
final List<int> members;
final List<int?> members;
final bool follow;
final bool canEveryoneAddMembers;
final Color color;
final Color? color;
const NewConversation({
@required this.name,
@required this.members,
@required this.follow,
@required this.canEveryoneAddMembers,
@required this.color,
}) : assert(members != null),
assert(members.length > 0),
assert(follow != null),
assert(canEveryoneAddMembers != null);
required this.name,
required this.members,
required this.follow,
required this.canEveryoneAddMembers,
required this.color,
}) :
assert(members.length > 0);
}

View File

@ -1,5 +1,4 @@
import 'package:comunic/models/api_request.dart';
import 'package:meta/meta.dart';
/// New conversation message model
///
@ -9,17 +8,16 @@ import 'package:meta/meta.dart';
class NewConversationMessage {
final int conversationID;
final String message;
final BytesFile file;
final BytesFile thumbnail;
final String? message;
final BytesFile? file;
final BytesFile? thumbnail;
NewConversationMessage({
@required this.conversationID,
@required this.message,
required this.conversationID,
required this.message,
this.file,
this.thumbnail,
}) : assert(conversationID != null),
assert(file != null || message != null);
}) : assert(file != null || message != null);
bool get hasMessage => message != null;

View File

@ -9,19 +9,16 @@ class NewConversationsSettings {
final bool following;
final bool isComplete;
final String name;
final bool canEveryoneAddMembers;
final Color color;
final bool? canEveryoneAddMembers;
final Color? color;
const NewConversationsSettings({
@required this.convID,
@required this.following,
@required this.isComplete,
@required this.name,
@required this.canEveryoneAddMembers,
@required this.color,
}) : assert(convID != null),
assert(convID > 0),
assert(following != null),
assert(isComplete != null),
required this.convID,
required this.following,
required this.isComplete,
required this.name,
required this.canEveryoneAddMembers,
required this.color,
}) : assert(convID > 0),
assert(!isComplete || canEveryoneAddMembers != null);
}

View File

@ -1,5 +1,3 @@
import 'package:flutter/material.dart';
import 'api_request.dart';
/// New emoji information
@ -11,8 +9,7 @@ class NewEmoji {
final BytesFile image;
const NewEmoji({
@required this.shortcut,
@required this.image,
}) : assert(shortcut != null),
assert(image != null);
required this.shortcut,
required this.image,
});
}

View File

@ -1,5 +1,4 @@
import 'package:comunic/models/group.dart';
import 'package:flutter/foundation.dart';
/// This class contains information about a conversation linked to a group
/// to create
@ -12,10 +11,8 @@ class NewGroupConversation {
final GroupMembershipLevel minMembershipLevel;
const NewGroupConversation({
@required this.groupID,
@required this.name,
@required this.minMembershipLevel,
}) : assert(groupID != null),
assert(name != null),
assert(minMembershipLevel != null);
required this.groupID,
required this.name,
required this.minMembershipLevel,
});
}

Some files were not shown because too many files have changed in this diff Show More