1
0
mirror of https://gitlab.com/comunic/comunicmobile synced 2024-11-22 04:49:21 +00:00

Start to fix null safety migration errors

This commit is contained in:
Pierre HUBERT 2022-03-10 19:39:57 +01:00
parent ab2c5da0da
commit 3a997cdc56
258 changed files with 2879 additions and 2912 deletions

View File

@ -8,16 +8,16 @@ import 'package:comunic/models/advanced_group_info.dart';
///
/// @author Pierre Hubert
AdvancedGroupInfo _forezGroup;
AdvancedGroupInfo? _forezGroup;
class ForezGroupHelper {
static Future<void> setId(int groupID) async {
(await PreferencesHelper.getInstance())
(await PreferencesHelper.getInstance())!
.setInt(PreferencesKeyList.FOREZ_GROUP, groupID);
}
static Future<int> getId() async {
return (await PreferencesHelper.getInstance())
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,8 +34,8 @@ class _JoinGroupPaneBody extends StatefulWidget {
final Function() onUpdated;
const _JoinGroupPaneBody({
Key key,
@required this.onUpdated,
Key? key,
required this.onUpdated,
}) : assert(onUpdated != null),
super(key: key);
@ -46,10 +46,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 +65,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 +112,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 +121,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

@ -24,8 +24,8 @@ class ForezMemberProfileRoute extends StatefulWidget {
final int userID;
const ForezMemberProfileRoute({
Key key,
@required this.userID,
Key? key,
required this.userID,
}) : assert(userID != null),
super(key: key);
@ -39,13 +39,13 @@ 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,
)
]),
@ -101,7 +101,7 @@ class _ForezMemberProfileRouteState extends State<ForezMemberProfileRoute> {
? Container()
: CachedNetworkImage(
fit: BoxFit.cover,
imageUrl: _user.accountImageURL,
imageUrl: _user.accountImageURL!,
height: _appBarHeight,
),
// This gradient ensures that the toolbar icons are distinct
@ -135,7 +135,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 +143,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 +153,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,7 +164,7 @@ 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),
@ -174,10 +174,10 @@ class _ForezMemberProfileRouteState extends State<ForezMemberProfileRoute> {
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,12 +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),

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);
@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,12 +201,12 @@ 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,
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),

View File

@ -26,14 +26,14 @@ enum CreateAccountResult {
class AccountHelper {
// Current user ID
static int _currentUserID = -1;
static int? _currentUserID = -1;
/// Checkout whether current user is signed in or not
///
/// Warning : This method MUST BE CALLED AT LEAST ONCE AFTER APP START !!!
Future<bool> signedIn() async {
bool signedIn =
(await PreferencesHelper.getInstance()).getLoginToken() != null;
(await PreferencesHelper.getInstance())!.getLoginToken() != null;
// Load current user ID for later use
if (signedIn && _currentUserID == -1) await _loadCurrentUserID();
@ -56,8 +56,8 @@ class AccountHelper {
else if (response.code != 200) return AuthResult.NETWORK_ERROR;
// Save login token
await (await PreferencesHelper.getInstance())
.setLoginToken(response.getObject()["token"]);
await (await PreferencesHelper.getInstance())!
.setLoginToken(response.getObject()!["token"]);
// Get current user ID
final userID = await _downloadCurrentUserID();
@ -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,27 +130,27 @@ 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"];
.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())
.getObject()["defined"];
.getObject()!["defined"];
/// 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())
.getObject()["questions"])
.getObject()!["questions"])
.cast<String>();
/// Validate given security answers
@ -158,14 +158,14 @@ 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",
answers.map((f) => Uri.encodeComponent(f)).join("&"))
.execWithThrow())
.getObject()["reset_token"];
.getObject()!["reset_token"];
/// Check a password reset token
///
@ -195,25 +195,26 @@ 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;
return response.getObject()["userID"];
return response.getObject()!["userID"];
}
/// Get the ID of the currently signed in user
Future<void> _loadCurrentUserID() async {
final preferences = await PreferencesHelper.getInstance();
final preferences =
await PreferencesHelper.getInstance();
_currentUserID = preferences.getInt(PreferencesKeyList.USER_ID);
}
/// 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

@ -23,7 +23,7 @@ class APIHelper {
//Add user token (if required)
if (request.needLogin) {
final token = (await PreferencesHelper.getInstance()).getLoginToken();
final token = (await PreferencesHelper.getInstance())!.getLoginToken();
if (token == null) {
EventsHelper.emit(InvalidLoginTokensEvent());
@ -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
///
@ -47,13 +46,13 @@ class ConversationsHelper {
.addBool("canEveryoneAddMembers", settings.canEveryoneAddMembers)
.execWithThrow();
return response.getObject()["conversationID"];
return response.getObject()!["conversationID"];
}
/// 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)
@ -71,7 +70,7 @@ class ConversationsHelper {
/// Update admin status of a user in a conversation
///
/// Throws in case of failure
static Future<void> setAdmin(int/*!*/ convID, int/*!*/ userID, bool admin) async =>
static Future<void> setAdmin(int convID, int userID, bool admin) async =>
await APIRequest.withLogin("conversations/setAdmin")
.addInt("convID", convID)
.addInt("userID", userID)
@ -91,7 +90,7 @@ class ConversationsHelper {
if (settings.isComplete)
request
.addString("name", settings.name ?? "")
.addBool("canEveryoneAddMembers", settings.canEveryoneAddMembers)
.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,13 +147,13 @@ 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,
args: {"conversationID": id.toString()}).execWithThrow();
final conversation = apiToConversation(response.getObject());
final conversation = apiToConversation(response.getObject()!);
await ConversationsSerializationHelper()
.insertOrReplaceElement((c) => c.id == conversation.id, conversation);
@ -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,
@ -211,7 +210,7 @@ class ConversationsHelper {
).execWithThrow();
// Get and return conversation ID
return int.parse(response.getObject()["conversationsID"][0].toString());
return int.parse(response.getObject()!["conversationsID"][0].toString());
}
/// Asynchronously get the name of the conversation
@ -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,12 +114,12 @@ 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),
@ -129,23 +129,23 @@ class NewCallSignalEvent {
/// 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
@ -108,7 +107,7 @@ class FriendsHelper {
if (response.code != 200)
throw Exception("Could not get friendship status!");
final obj = response.getObject();
final obj = response.getObject()!;
return FriendStatus(
userID: userID,
@ -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,7 +49,7 @@ enum GetAdvancedInfoStatus { SUCCESS, ACCESS_DENIED }
class GetAdvancedInfoResult {
final GetAdvancedInfoStatus status;
final AdvancedGroupInfo info;
final AdvancedGroupInfo? info;
GetAdvancedInfoResult(this.status, this.info) : assert(status != null);
}
@ -57,7 +57,7 @@ class GetAdvancedInfoResult {
/// 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,
@ -69,7 +69,7 @@ class GroupsHelper {
final list = GroupsList();
response
.getObject()
.getObject()!
.forEach((k, d) => list[int.parse(k)] = getGroupFromAPI(d));
return list;
@ -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)
@ -189,7 +189,7 @@ class GroupsHelper {
case 200:
return GetAdvancedInfoResult(GetAdvancedInfoStatus.SUCCESS,
_getAdvancedGroupInfoFromAPI(result.getObject()));
_getAdvancedGroupInfoFromAPI(result.getObject()!));
default:
throw Exception("Could not get advanced group information!");
@ -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

@ -55,7 +55,7 @@ class NotificationsHelper {
await APIRequest(uri: "notifications/count_all_news", needLogin: true)
.exec();
final content = response.assertOk().getObject();
final content = response.assertOk().getObject()!;
return CountUnreadNotifications(
notifications: content["notifications"],
@ -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;
@ -120,7 +123,7 @@ class PostsHelper {
if (!response.isOK)
throw Exception("Could not get information about the post!");
return _apiToPost(response.getObject());
return _apiToPost(response.getObject()!);
}
/// Create a new post
@ -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:
@ -221,7 +225,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 +236,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 +246,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 +267,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 +280,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
@ -47,13 +47,13 @@ class PushNotificationsHelper {
response["independent_push_url"]);
}
await (await PreferencesHelper.getInstance()).setString(
await (await PreferencesHelper.getInstance())!.setString(
PreferencesKeyList.PUSH_NOTIFICATIONS_STATUS, response["status"]);
}
/// Clear local push notifications status
static Future<void> clearLocalStatus() async {
await (await PreferencesHelper.getInstance())
await (await PreferencesHelper.getInstance())!
.removeKey(PreferencesKeyList.PUSH_NOTIFICATIONS_STATUS);
// Stop local refresh notification refresh
@ -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

@ -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,7 +8,7 @@ import 'package:comunic/models/conversation_message.dart';
///
/// @author Pierre Hubert
HashMap<int, ConversationsMessagesSerializationHelper> _instances;
HashMap<int?, ConversationsMessagesSerializationHelper>? _instances;
class ConversationsMessagesSerializationHelper
extends BaseSerializationHelper<ConversationMessage> {
@ -18,13 +18,13 @@ class ConversationsMessagesSerializationHelper
: convID = convID,
assert(convID != null);
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 {
@ -15,7 +15,7 @@ class ServerConfigurationHelper {
final response =
(await APIRequest.withoutLogin("server/config").execWithThrow())
.getObject();
.getObject()!;
final banner = response["banner"];
final pushNotificationsPolicy = response["push_notifications"];
@ -97,7 +97,7 @@ class ServerConfigurationHelper {
}
/// 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 +107,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

@ -25,7 +25,7 @@ class SettingsHelper {
final response =
(await APIRequest(uri: "settings/get_general", needLogin: true).exec())
.assertOk()
.getObject();
.getObject()!;
return GeneralSettings(
email: response["email"],
@ -88,13 +88,13 @@ class SettingsHelper {
(await APIRequest(uri: "settings/get_account_image", needLogin: true)
.exec())
.assertOk()
.getObject();
.getObject()!;
return AccountImageSettings(
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())
@ -175,7 +175,7 @@ class SettingsHelper {
(await APIRequest(uri: "settings/get_security", needLogin: true)
.addString("password", password)
.execWithThrow())
.getObject();
.getObject()!;
return SecuritySettings(
securityQuestion1: response["security_question_1"],
@ -207,7 +207,7 @@ class SettingsHelper {
final response =
(await APIRequest.withLogin("settings/get_data_conservation_policy")
.execWithThrow())
.getObject();
.getObject()!;
return DataConservationPolicySettings(
inactiveAccountLifeTime: response["inactive_account_lifetime"],
@ -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
///
@ -13,7 +12,7 @@ class SurveyHelper {
apiToSurvey((await APIRequest.withLogin("surveys/get_info")
.addInt("postID", postID)
.execWithThrow())
.getObject());
.getObject()!);
/// Cancel the response of a user to a survey
Future<bool> cancelResponse(Survey survey) async {
@ -26,7 +25,7 @@ class SurveyHelper {
/// Send the response of a user to a survey
Future<bool> respondToSurvey(
{@required Survey survey, @required SurveyChoice choice}) async {
{required Survey survey, required SurveyChoice choice}) async {
assert(survey != null);
assert(choice != null);

View File

@ -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",
@ -42,7 +42,7 @@ class UsersHelper {
if (response.code != 200) return null;
final list = UsersList();
response.getObject().forEach(
response.getObject()!.forEach(
(k, v) => list.add(
User(
id: v["userID"],
@ -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(
@ -151,7 +151,7 @@ class UsersHelper {
throw new GetUserAdvancedUserError(cause);
}
return apiToAdvancedUserInfo(response.getObject());
return apiToAdvancedUserInfo(response.getObject()!);
}
/// Parse the list of custom emojies

View File

@ -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,10 +8,10 @@ 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);
}
@ -30,8 +29,8 @@ class VirtualDirectoryHelper {
return VirtualDirectoryResult(type: VirtualDirectoryType.NONE);
case 200:
final id = response.getObject()["id"];
final kind = response.getObject()["kind"];
final id = response.getObject()!["id"];
final kind = response.getObject()!["kind"];
switch (kind) {
case "user":
return VirtualDirectoryResult(

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,14 +23,14 @@ 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"];
.getObject()!["token"];
/// Connect to WebSocket
static connect() async {
@ -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,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

@ -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

@ -22,23 +22,23 @@ class PostsList extends ListBase<Post> {
void operator []=(int index, Post value) => _list[index] = value;
// 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);

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

@ -44,8 +44,8 @@ class ComunicApplication extends StatefulWidget {
final PreferencesHelper preferences;
const ComunicApplication({
Key key,
@required this.preferences,
Key? key,
required this.preferences,
}) : assert(preferences != null),
super(key: key);

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,4 +1,4 @@
import 'package:flutter/widgets.dart';
/// Account image settings
///
@ -7,14 +7,14 @@ import 'package:flutter/widgets.dart';
enum AccountImageVisibilityLevels { EVERYONE, COMUNIC_USERS, FRIENDS_ONLY }
class AccountImageSettings {
final bool/*!*/ hasImage;
final String/*!*/ imageURL;
final AccountImageVisibilityLevels/*!*/ visibility;
final bool hasImage;
final String imageURL;
final AccountImageVisibilityLevels visibility;
const AccountImageSettings({
@required this.hasImage,
@required this.imageURL,
@required this.visibility,
required this.hasImage,
required this.imageURL,
required this.visibility,
}) : assert(hasImage != null),
assert(imageURL != null),
assert(visibility != null);

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,34 +9,34 @@ 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;
bool/*!*/ isForezGroup;
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,
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(
id: id,

View File

@ -3,42 +3,41 @@ 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/*!*/ publicNote;
final bool/*!*/ canPostTexts;
final bool/*!*/ isFriendsListPublic;
final int/*!*/ numberFriends;
final int/*!*/ accountCreationTime;
final String/*!*/ personalWebsite;
final String location;
bool/*!*/ userLike;
int/*!*/ likes;
final String? emailAddress;
final String publicNote;
final bool canPostTexts;
final bool isFriendsListPublic;
final int numberFriends;
final int accountCreationTime;
final String personalWebsite;
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,
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),
@ -60,9 +59,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,
@ -25,15 +24,15 @@ class BytesFile {
}
class APIRequest {
final String/*!*/ uri;
final bool/*!*/ needLogin;
ProgressCallback progressCallback;
CancelToken cancelToken;
Map<String, String> args;
final String uri;
final bool needLogin;
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})
APIRequest({required this.uri, this.needLogin = false, this.args})
: assert(uri != null),
assert(needLogin != null) {
if (this.args == null) this.args = Map();
@ -51,18 +50,18 @@ class APIRequest {
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 +70,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

@ -5,14 +5,14 @@ import 'dart:convert';
/// @author Pierre HUBERT
class APIResponse {
final int/*!*/ code;
final String content;
final int code;
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,18 +1,18 @@
import 'package:flutter/cupertino.dart';
/// Application settings
///
/// @author Pierre Hubert
class ApplicationPreferences {
bool/*!*/ enableDarkMode;
bool/*!*/ forceMobileMode;
bool/*!*/ showPerformancesOverlay;
bool enableDarkMode;
bool forceMobileMode;
bool showPerformancesOverlay;
ApplicationPreferences({
@required this.enableDarkMode,
@required this.forceMobileMode,
@required this.showPerformancesOverlay,
required this.enableDarkMode,
required this.forceMobileMode,
required this.showPerformancesOverlay,
}) : assert(enableDarkMode != null),
assert(forceMobileMode != null),
assert(showPerformancesOverlay != null);

View File

@ -1,14 +1,14 @@
import 'package:meta/meta.dart';
/// Authentication details
///
/// @author Pierre HUBERT
class AuthenticationDetails {
final String/*!*/ email;
final String/*!*/ password;
final String email;
final String password;
const AuthenticationDetails({@required this.email, @required this.password})
const AuthenticationDetails({required this.email, required this.password})
: assert(email != null),
assert(password != null);
}

View File

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

View File

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

View File

@ -1,4 +1,3 @@
import 'package:flutter/material.dart';
import 'package:flutter_webrtc/flutter_webrtc.dart';
/// Single call member information
@ -8,16 +7,16 @@ import 'package:flutter_webrtc/flutter_webrtc.dart';
enum MemberStatus { JOINED, READY }
class CallMember {
final int/*!*/ userID;
MemberStatus/*!*/ status;
MediaStream stream;
final int userID;
MemberStatus status;
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
///
@ -11,24 +10,24 @@ import 'package:meta/meta.dart';
/// @author Pierre HUBERT
class Comment implements LikeElement {
final int/*!*/ id;
final int/*!*/ userID;
final int/*!*/ postID;
final int/*!*/ timeSent;
DisplayedString/*!*/ content;
final String imageURL;
int/*!*/ likes;
bool/*!*/ userLike;
final int id;
final int userID;
final int postID;
final int timeSent;
DisplayedString content;
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,
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),

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,
@ -55,9 +55,9 @@ class Config {
assert(appName != null);
/// Get and set static configuration
static Config/*?*/ _config;
static Config? _config;
static Config get() {
static Config? get() {
return _config;
}
@ -67,6 +67,6 @@ class Config {
}
/// Get the current configuration of the application
Config/*!*/ config() {
return Config.get();
Config config() {
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,28 +13,28 @@ 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*/ @required this.id,
/*required*/ @required this.lastActivity,
@required this.name,
@required this.color,
@required this.logoURL,
@required this.groupID,
@required this.groupMinMembershipLevel,
/*required*/ @required this.members,
/*required*/ @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),
@ -49,7 +50,7 @@ class Conversation extends SerializableElement<Conversation> {
/// 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 +62,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 +87,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 +109,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,22 +1,22 @@
import 'package:flutter/widgets.dart';
/// Conversation member
///
/// @author Pierre Hubert
class ConversationMember {
final int/*!*/ userID;
final int/*!*/ lastMessageSeen;
final int/*!*/ lastAccessTime;
final bool/*!*/ following;
final bool/*!*/ isAdmin;
final int userID;
final int lastMessageSeen;
final int lastAccessTime;
final bool following;
final bool isAdmin;
const ConversationMember({
/*required*/ @required this.userID,
/*required*/ @required this.lastMessageSeen,
/*required*/ @required this.lastAccessTime,
/*required*/ @required this.following,
/*required*/ @required this.isAdmin,
/*required*/ required this.userID,
/*required*/ required this.lastMessageSeen,
/*required*/ required this.lastAccessTime,
/*required*/ required this.following,
/*required*/ required this.isAdmin,
}) : assert(userID != null),
assert(lastMessageSeen != null),
assert(lastAccessTime != null),

View File

@ -30,27 +30,27 @@ 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,
required String this.url,
required int this.size,
required String this.name,
required this.thumbnail,
required String this.type,
}) : assert(url != null),
assert(size != null),
assert(name != null),
assert(type != null);
/// 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,19 +102,19 @@ 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,
required this.type,
required this.userID,
required this.userWhoAdded,
required this.userAdded,
required this.userWhoRemoved,
required this.userRemoved,
}) : assert(type != null),
assert(userID != null ||
(type != ConversationServerMessageType.USER_CREATED_CONVERSATION &&
@ -124,7 +124,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:
@ -144,26 +144,26 @@ class ConversationServerMessage {
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,
});
}
@ -191,29 +191,29 @@ 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,
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(id != null),
assert(convID != null),
assert(userID != null || serverMessage != null),
assert(timeSent != null),
assert(message != null || 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 +224,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,12 @@
/// @author Pierre Hubert
class CountUnreadNotifications {
int notifications;
int conversations;
int? notifications;
int? conversations;
CountUnreadNotifications({
this.notifications,
this.conversations,
required int this.notifications,
required int this.conversations,
}) : assert(notifications != null),
assert(conversations != null);
}

View File

@ -1,20 +1,20 @@
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,
required int this.id,
required int this.userID,
required String this.shortcut,
required String this.url,
}) : assert(id != null),
assert(userID != null),
assert(shortcut != null),

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,4 +1,4 @@
import 'package:flutter/material.dart';
/// Single presence information
///
@ -11,10 +11,10 @@ class Presence {
final int day;
const Presence({
@required this.userID,
@required this.year,
@required this.month,
@required this.day,
required this.userID,
required this.year,
required this.month,
required this.day,
}) : assert(userID != null),
assert(year != null),
assert(month != null),

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,16 +8,16 @@ 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,
required int id,
required this.accepted,
required int this.lastActive,
required this.following,
required this.canPostTexts,
}) : assert(id != null),
assert(accepted != null),
assert(lastActive != null),
@ -27,10 +26,10 @@ class Friend extends CacheModel implements Comparable<Friend> {
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() => {

View File

@ -1,4 +1,4 @@
import 'package:meta/meta.dart';
/// Simple friendship status
///
@ -12,11 +12,11 @@ class FriendStatus {
final bool following;
const FriendStatus({
@required this.userID,
@required this.areFriend,
@required this.sentRequest,
@required this.receivedRequest,
@required this.following,
required this.userID,
required this.areFriend,
required this.sentRequest,
required this.receivedRequest,
required this.following,
}) : assert(userID != null),
assert(areFriend != null),
assert(sentRequest != null),

View File

@ -1,5 +1,4 @@
import 'package:comunic/enums/user_page_visibility.dart';
import 'package:flutter/material.dart';
/// General settings
///
@ -18,22 +17,22 @@ 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,
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),

View File

@ -1,5 +1,4 @@
import 'package:comunic/utils/intl_utils.dart';
import 'package:meta/meta.dart';
/// Group information
///
@ -14,20 +13,20 @@ enum GroupMembershipLevel {
VISITOR
}
String/*!*/ membershipToText(GroupMembershipLevel level) {
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!");
}
@ -51,16 +50,16 @@ 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,
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),

View File

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

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,40 @@ 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);
Membership.friend(this.friend)
Membership.friend(Friend this.friend)
: type = MembershipType.FRIEND,
conversation = null,
groupID = null,
groupLastActive = null,
assert(friend != 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);
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,4 +1,4 @@
import 'package:flutter/widgets.dart';
/// New account information container
///
@ -11,10 +11,10 @@ class NewAccount {
final String password;
NewAccount({
@required this.firstName,
@required this.lastName,
@required this.email,
@required this.password,
required this.firstName,
required this.lastName,
required this.email,
required this.password,
}) : assert(firstName != null),
assert(lastName != null),
assert(email != null),

View File

@ -1,5 +1,3 @@
import 'package:meta/meta.dart';
import 'api_request.dart';
/// New comment information
@ -9,12 +7,12 @@ 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,
required this.postID,
required this.content,
required this.image,
}) : assert(postID != null);
bool get hasContent => content != null && content.length > 0;

View File

@ -6,17 +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,
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),

View File

@ -1,5 +1,4 @@
import 'package:comunic/models/api_request.dart';
import 'package:meta/meta.dart';
/// New conversation message model
///
@ -9,13 +8,13 @@ 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),

View File

@ -9,16 +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,
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),

View File

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

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,9 +11,9 @@ class NewGroupConversation {
final GroupMembershipLevel minMembershipLevel;
const NewGroupConversation({
@required this.groupID,
@required this.name,
@required this.minMembershipLevel,
required this.groupID,
required this.name,
required this.minMembershipLevel,
}) : assert(groupID != null),
assert(name != null),
assert(minMembershipLevel != null);

View File

@ -1,7 +1,6 @@
import 'package:comunic/enums/post_kind.dart';
import 'package:comunic/enums/post_target.dart';
import 'package:comunic/enums/post_visibility_level.dart';
import 'package:meta/meta.dart';
import 'api_request.dart';
@ -15,9 +14,9 @@ class NewSurvey {
final bool allowNewChoicesCreation;
const NewSurvey({
@required this.question,
@required this.answers,
@required this.allowNewChoicesCreation,
required this.question,
required this.answers,
required this.allowNewChoicesCreation,
}) : assert(question != null),
assert(answers.length > 1),
assert(allowNewChoicesCreation != null);
@ -28,26 +27,26 @@ class NewPost {
final int targetID;
final PostVisibilityLevel visibility;
final String content;
final BytesFile image;
final String url;
final List<int> pdf;
final BytesFile? image;
final String? url;
final List<int>? pdf;
final PostKind kind;
final DateTime timeEnd;
final NewSurvey survey;
final String youtubeId;
final DateTime? timeEnd;
final NewSurvey? survey;
final String? youtubeId;
const NewPost({
@required this.target,
@required this.targetID,
@required this.visibility,
@required this.content,
@required this.kind,
@required this.image,
@required this.url,
@required this.pdf,
@required this.timeEnd,
@required this.survey,
@required this.youtubeId,
required this.target,
required this.targetID,
required this.visibility,
required this.content,
required this.kind,
required this.image,
required this.url,
required this.pdf,
required this.timeEnd,
required this.survey,
required this.youtubeId,
}) : assert(target != null),
assert(targetID != null),
assert(visibility != null),

View File

@ -1,4 +1,4 @@
import 'package:flutter/widgets.dart';
/// Notification model
///
@ -45,19 +45,19 @@ class Notification {
final int onElemId;
final NotificationElementType onElemType;
final NotificationType type;
final int fromContainerId;
final NotificationElementType fromContainerType;
final int? fromContainerId;
final NotificationElementType? fromContainerType;
const Notification({
@required this.id,
@required this.timeCreate,
@required this.seen,
@required this.fromUser,
@required this.onElemId,
@required this.onElemType,
@required this.type,
@required this.fromContainerId,
@required this.fromContainerType,
required this.id,
required this.timeCreate,
required this.seen,
required this.fromUser,
required this.onElemId,
required this.onElemType,
required this.type,
required this.fromContainerId,
required this.fromContainerType,
}) : assert(id != null),
assert(timeCreate != null),
assert(seen != null),

View File

@ -1,4 +1,4 @@
import 'package:flutter/material.dart';
/// Notifications settings
///
@ -9,8 +9,8 @@ class NotificationsSettings {
bool allowNotificationsSound;
NotificationsSettings({
@required this.allowConversations,
@required this.allowNotificationsSound,
required this.allowConversations,
required this.allowNotificationsSound,
}) : assert(allowConversations != null),
assert(allowNotificationsSound != null);
}

View File

@ -6,7 +6,6 @@ import 'package:comunic/lists/comments_list.dart';
import 'package:comunic/models/displayed_content.dart';
import 'package:comunic/models/like_element.dart';
import 'package:comunic/models/survey.dart';
import 'package:meta/meta.dart';
/// Single post information
///
@ -15,50 +14,50 @@ import 'package:meta/meta.dart';
class Post implements LikeElement {
final int id;
final int userID;
final int userPageID;
final int groupID;
final int? userPageID;
final int? groupID;
final int timeSent;
DisplayedString content;
PostVisibilityLevel visibilityLevel;
final PostKind kind;
final int fileSize;
final String fileType;
final String filePath;
final String fileURL;
final int timeEnd;
final String linkURL;
final String linkTitle;
final String linkDescription;
final String linkImage;
final int? fileSize;
final String? fileType;
final String? filePath;
final String? fileURL;
final int? timeEnd;
final String? linkURL;
final String? linkTitle;
final String? linkDescription;
final String? linkImage;
int likes;
bool userLike;
final UserAccessLevels access;
final CommentsList comments;
Survey survey;
final CommentsList? comments;
Survey? survey;
Post(
{@required this.id,
@required this.userID,
@required this.userPageID,
@required this.groupID,
@required this.timeSent,
@required this.content,
@required this.visibilityLevel,
@required this.kind,
@required this.fileSize,
@required this.fileType,
@required this.filePath,
@required this.fileURL,
@required this.timeEnd,
@required this.linkURL,
@required this.linkTitle,
@required this.linkDescription,
@required this.linkImage,
@required this.likes,
@required this.userLike,
@required this.access,
@required this.comments,
@required this.survey})
{required this.id,
required this.userID,
required this.userPageID,
required this.groupID,
required this.timeSent,
required this.content,
required this.visibilityLevel,
required this.kind,
required this.fileSize,
required this.fileType,
required this.filePath,
required this.fileURL,
required this.timeEnd,
required this.linkURL,
required this.linkTitle,
required this.linkDescription,
required this.linkImage,
required this.likes,
required this.userLike,
required this.access,
required this.comments,
required this.survey})
: assert(id != null),
assert(userID != null),
assert(userPageID != 0 || groupID != 0),
@ -72,7 +71,7 @@ class Post implements LikeElement {
assert(userLike != null),
assert(access != null);
bool get isGroupPost => groupID != null && groupID > 0;
bool get isGroupPost => groupID != null && groupID! > 0;
bool get hasContent => content != null && !content.isNull;

View File

@ -1,4 +1,4 @@
import 'package:flutter/material.dart';
/// Check password reset token result
///
@ -10,9 +10,9 @@ class ResCheckPasswordToken {
final String email;
const ResCheckPasswordToken({
@required this.firstName,
@required this.lastName,
@required this.email,
required this.firstName,
required this.lastName,
required this.email,
}) : assert(firstName != null),
assert(lastName != null),
assert(email != null);

View File

@ -1,4 +1,4 @@
import 'package:flutter/cupertino.dart';
/// Single search result
///
@ -11,8 +11,8 @@ class SearchResult {
final SearchResultKind kind;
SearchResult({
@required this.id,
@required this.kind,
required this.id,
required this.kind,
}) : assert(id != null),
assert(kind != null);
}

View File

@ -1,4 +1,4 @@
import 'package:flutter/material.dart';
/// Security settings of the user
///
@ -11,10 +11,10 @@ class SecuritySettings {
final String securityAnswer2;
const SecuritySettings({
@required this.securityQuestion1,
@required this.securityAnswer1,
@required this.securityQuestion2,
@required this.securityAnswer2,
required this.securityQuestion1,
required this.securityAnswer1,
required this.securityQuestion2,
required this.securityAnswer2,
}) : assert(securityQuestion1 != null),
assert(securityAnswer1 != null),
assert(securityQuestion2 != null),

View File

@ -1,5 +1,4 @@
import 'package:comunic/utils/date_utils.dart';
import 'package:flutter/widgets.dart';
import 'package:version/version.dart';
/// Server static configuration
@ -11,8 +10,8 @@ class NotificationsPolicy {
final bool hasIndependent;
const NotificationsPolicy({
@required this.hasFirebase,
@required this.hasIndependent,
required this.hasFirebase,
required this.hasIndependent,
}) : assert(hasFirebase != null),
assert(hasIndependent != null);
}
@ -28,14 +27,14 @@ class PasswordPolicy {
final int minCategoriesPresence;
const PasswordPolicy({
@required this.allowMailInPassword,
@required this.allowNameInPassword,
@required this.minPasswordLength,
@required this.minNumberUpperCaseLetters,
@required this.minNumberLowerCaseLetters,
@required this.minNumberDigits,
@required this.minNumberSpecialCharacters,
@required this.minCategoriesPresence,
required this.allowMailInPassword,
required this.allowNameInPassword,
required this.minPasswordLength,
required this.minNumberUpperCaseLetters,
required this.minNumberLowerCaseLetters,
required this.minNumberDigits,
required this.minNumberSpecialCharacters,
required this.minCategoriesPresence,
}) : assert(allowMailInPassword != null),
assert(allowNameInPassword != null),
assert(minPasswordLength != null),
@ -55,12 +54,12 @@ class ServerDataConservationPolicy {
final int minLikesLifetime;
const ServerDataConservationPolicy({
@required this.minInactiveAccountLifetime,
@required this.minNotificationLifetime,
@required this.minCommentsLifetime,
@required this.minPostsLifetime,
@required this.minConversationMessagesLifetime,
@required this.minLikesLifetime,
required this.minInactiveAccountLifetime,
required this.minNotificationLifetime,
required this.minCommentsLifetime,
required this.minPostsLifetime,
required this.minConversationMessagesLifetime,
required this.minLikesLifetime,
}) : assert(minInactiveAccountLifetime != null),
assert(minNotificationLifetime != null),
assert(minCommentsLifetime != null),
@ -85,19 +84,19 @@ class ConversationsPolicy {
final int maxLogoHeight;
const ConversationsPolicy({
@required this.maxConversationNameLen,
@required this.minMessageLen,
@required this.maxMessageLen,
@required this.allowedFilesType,
@required this.filesMaxSize,
@required this.writingEventInterval,
@required this.writingEventLifetime,
@required this.maxMessageImageWidth,
@required this.maxMessageImageHeight,
@required this.maxThumbnailWidth,
@required this.maxThumbnailHeight,
@required this.maxLogoWidth,
@required this.maxLogoHeight,
required this.maxConversationNameLen,
required this.minMessageLen,
required this.maxMessageLen,
required this.allowedFilesType,
required this.filesMaxSize,
required this.writingEventInterval,
required this.writingEventLifetime,
required this.maxMessageImageWidth,
required this.maxMessageImageHeight,
required this.maxThumbnailWidth,
required this.maxThumbnailHeight,
required this.maxLogoWidth,
required this.maxLogoHeight,
}) : assert(maxConversationNameLen != null),
assert(minMessageLen != null),
assert(maxMessageLen != null),
@ -121,11 +120,11 @@ class AccountInformationPolicy {
final int maxLocationLength;
const AccountInformationPolicy({
@required this.minFirstNameLength,
@required this.maxFirstNameLength,
@required this.minLastNameLength,
@required this.maxLastNameLength,
@required this.maxLocationLength,
required this.minFirstNameLength,
required this.maxFirstNameLength,
required this.minLastNameLength,
required this.maxLastNameLength,
required this.maxLocationLength,
}) : assert(minFirstNameLength != null),
assert(maxFirstNameLength != null),
assert(minLastNameLength != null),
@ -136,7 +135,7 @@ class AccountInformationPolicy {
enum BannerNature { Information, Warning, Success }
extension BannerNatureExt on BannerNature {
static BannerNature fromStr(String s) {
static BannerNature fromStr(String? s) {
switch (s) {
case "information":
return BannerNature.Information;
@ -151,22 +150,22 @@ extension BannerNatureExt on BannerNature {
class Banner {
final bool enabled;
final int expire;
final int? expire;
final BannerNature nature;
final Map<String, String> message;
final String link;
final String? link;
const Banner({
@required this.enabled,
@required this.expire,
@required this.nature,
@required this.message,
@required this.link,
required this.enabled,
required this.expire,
required this.nature,
required this.message,
required this.link,
}) : assert(enabled != null),
assert(nature != null),
assert(message != null);
bool get visible => enabled && (expire == null || expire > time());
bool get visible => enabled && (expire == null || expire! > time());
}
class ServerConfig {
@ -176,7 +175,7 @@ class ServerConfig {
final String contactEmail;
final String playStoreURL;
final String androidDirectDownloadURL;
final Banner banner;
final Banner? banner;
final NotificationsPolicy notificationsPolicy;
final PasswordPolicy passwordPolicy;
final ServerDataConservationPolicy dataConservationPolicy;
@ -184,18 +183,18 @@ class ServerConfig {
final AccountInformationPolicy accountInformationPolicy;
const ServerConfig({
@required this.minSupportedMobileVersion,
@required this.termsURL,
@required this.privacyPolicyURL,
@required this.contactEmail,
@required this.playStoreURL,
@required this.androidDirectDownloadURL,
@required this.banner,
@required this.notificationsPolicy,
@required this.passwordPolicy,
@required this.dataConservationPolicy,
@required this.conversationsPolicy,
@required this.accountInformationPolicy,
required this.minSupportedMobileVersion,
required this.termsURL,
required this.privacyPolicyURL,
required this.contactEmail,
required this.playStoreURL,
required this.androidDirectDownloadURL,
required this.banner,
required this.notificationsPolicy,
required this.passwordPolicy,
required this.dataConservationPolicy,
required this.conversationsPolicy,
required this.accountInformationPolicy,
}) : assert(minSupportedMobileVersion != null),
assert(termsURL != null),
assert(privacyPolicyURL != null),

View File

@ -1,6 +1,5 @@
import 'package:comunic/models/survey_choice.dart';
import 'package:comunic/utils/account_utils.dart' as account;
import 'package:meta/meta.dart';
/// Survey information
///
@ -17,14 +16,14 @@ class Survey {
bool allowNewChoicesCreation;
Survey({
@required this.id,
@required this.userID,
@required this.postID,
@required this.creationTime,
@required this.question,
@required this.userChoice,
@required this.choices,
@required this.allowNewChoicesCreation,
required this.id,
required this.userID,
required this.postID,
required this.creationTime,
required this.question,
required this.userChoice,
required this.choices,
required this.allowNewChoicesCreation,
}) : assert(id != null),
assert(userID != null),
assert(postID != null),
@ -43,14 +42,14 @@ class Survey {
bool get canBlockNewChoicesCreation =>
allowNewChoicesCreation && account.userID() == this.userID;
SurveyChoice get userResponse {
SurveyChoice? get userResponse {
if (!hasResponded) return null;
return choices.firstWhere((e) => e.id == userChoice);
}
void cancelUserResponse() {
if (hasResponded) userResponse.responses--;
if (hasResponded) userResponse!.responses--;
userChoice = 0;
}

View File

@ -1,4 +1,4 @@
import 'package:meta/meta.dart';
/// Single survey choice
///
@ -10,9 +10,9 @@ class SurveyChoice {
int responses;
SurveyChoice({
@required this.id,
@required this.name,
@required this.responses,
required this.id,
required this.name,
required this.responses,
}) : assert(id != null),
assert(name != null),
assert(responses != null);

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