mirror of
https://gitlab.com/comunic/comunicmobile
synced 2025-06-20 16:55:17 +00:00
Start to fix null safety migration errors
This commit is contained in:
@ -4,14 +4,14 @@ import 'package:flutter/material.dart';
|
||||
///
|
||||
/// @author Pierre Hubert
|
||||
|
||||
Future<void> alert(BuildContext context, String msg) async {
|
||||
await showDialog(context: context, builder: (c) => _AlertDialog(msg: msg));
|
||||
Future<void> alert(BuildContext context, String? msg) async {
|
||||
await showDialog(context: context, builder: (c) => _AlertDialog(msg: msg!));
|
||||
}
|
||||
|
||||
class _AlertDialog extends StatelessWidget {
|
||||
final String msg;
|
||||
|
||||
const _AlertDialog({Key key, @required this.msg})
|
||||
const _AlertDialog({Key? key, required this.msg})
|
||||
: assert(msg != null),
|
||||
super(key: key);
|
||||
|
||||
|
@ -9,16 +9,16 @@ import 'package:video_player/video_player.dart';
|
||||
/// @author Pierre Hubert
|
||||
|
||||
/// Show audio player dialog
|
||||
Future<void> showAudioPlayerDialog(BuildContext context, String url) async {
|
||||
showDialog(context: context, builder: (c) => _AudioPlayerDialog(url: url));
|
||||
Future<void> showAudioPlayerDialog(BuildContext context, String? url) async {
|
||||
showDialog(context: context, builder: (c) => _AudioPlayerDialog(url: url!));
|
||||
}
|
||||
|
||||
class _AudioPlayerDialog extends StatefulWidget {
|
||||
final String url;
|
||||
|
||||
const _AudioPlayerDialog({
|
||||
Key key,
|
||||
@required this.url,
|
||||
Key? key,
|
||||
required this.url,
|
||||
}) : assert(url != null),
|
||||
super(key: key);
|
||||
|
||||
@ -27,16 +27,16 @@ class _AudioPlayerDialog extends StatefulWidget {
|
||||
}
|
||||
|
||||
class __AudioPlayerDialogState extends State<_AudioPlayerDialog> {
|
||||
VideoPlayerController _videoPlayerController;
|
||||
ChewieAudioController _chewieAudioController;
|
||||
VideoPlayerController? _videoPlayerController;
|
||||
ChewieAudioController? _chewieAudioController;
|
||||
|
||||
Future<void> _initialize() async {
|
||||
_videoPlayerController = VideoPlayerController.network(widget.url);
|
||||
|
||||
await _videoPlayerController.initialize();
|
||||
await _videoPlayerController!.initialize();
|
||||
|
||||
_chewieAudioController = ChewieAudioController(
|
||||
videoPlayerController: _videoPlayerController,
|
||||
videoPlayerController: _videoPlayerController!,
|
||||
autoPlay: true,
|
||||
looping: false,
|
||||
);
|
||||
@ -48,20 +48,20 @@ class __AudioPlayerDialogState extends State<_AudioPlayerDialog> {
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
if (_videoPlayerController != null) _videoPlayerController.dispose();
|
||||
if (_chewieAudioController != null) _chewieAudioController.dispose();
|
||||
if (_videoPlayerController != null) _videoPlayerController!.dispose();
|
||||
if (_chewieAudioController != null) _chewieAudioController!.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: Text(tr("Audio Player")),
|
||||
title: Text(tr("Audio Player")!),
|
||||
content: _buildContent(),
|
||||
actions: [
|
||||
MaterialButton(
|
||||
onPressed: _closeDialog,
|
||||
child: Text(tr("Close").toUpperCase()),
|
||||
child: Text(tr("Close")!.toUpperCase()),
|
||||
)
|
||||
],
|
||||
);
|
||||
@ -72,10 +72,10 @@ class __AudioPlayerDialogState extends State<_AudioPlayerDialog> {
|
||||
child: AsyncScreenWidget(
|
||||
onReload: _initialize,
|
||||
onBuild: _buildReadyContent,
|
||||
errorMessage: tr("Failed to initialize audio player!"),
|
||||
errorMessage: tr("Failed to initialize audio player!")!,
|
||||
),
|
||||
);
|
||||
|
||||
Widget _buildReadyContent() =>
|
||||
ChewieAudio(controller: _chewieAudioController);
|
||||
ChewieAudio(controller: _chewieAudioController!);
|
||||
}
|
||||
|
@ -6,17 +6,17 @@ import 'package:flutter_colorpicker/flutter_colorpicker.dart';
|
||||
///
|
||||
/// @author Pierre Hubert
|
||||
|
||||
Future<Color> showColorPickerDialog(
|
||||
BuildContext context, Color initialColor) async =>
|
||||
Future<Color?> showColorPickerDialog(
|
||||
BuildContext context, Color? initialColor) async =>
|
||||
await showDialog(
|
||||
context: context,
|
||||
builder: (c) => _ColorPickerDialog(initialColor: initialColor),
|
||||
);
|
||||
|
||||
class _ColorPickerDialog extends StatefulWidget {
|
||||
final Color initialColor;
|
||||
final Color? initialColor;
|
||||
|
||||
const _ColorPickerDialog({Key key, @required this.initialColor})
|
||||
const _ColorPickerDialog({Key? key, required this.initialColor})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
@ -24,7 +24,7 @@ class _ColorPickerDialog extends StatefulWidget {
|
||||
}
|
||||
|
||||
class __ColorPickerDialogState extends State<_ColorPickerDialog> {
|
||||
Color _newColor;
|
||||
Color? _newColor;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@ -42,11 +42,11 @@ class __ColorPickerDialogState extends State<_ColorPickerDialog> {
|
||||
actions: [
|
||||
MaterialButton(
|
||||
onPressed: () => Navigator.pop(context, widget.initialColor),
|
||||
child: Text(tr("Cancel").toUpperCase()),
|
||||
child: Text(tr("Cancel")!.toUpperCase()),
|
||||
),
|
||||
MaterialButton(
|
||||
onPressed: () => Navigator.pop(context, _newColor),
|
||||
child: Text(tr("Ok").toUpperCase()),
|
||||
child: Text(tr("Ok")!.toUpperCase()),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
@ -16,23 +16,23 @@ class _DeprecationDialog extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: Text(tr("Deprecated application version")),
|
||||
title: Text(tr("Deprecated application version")!),
|
||||
content: Text(tr(
|
||||
"This version of the Comunic application is deprecated. You might still be able to use it, but some features may not work. We recommend you to update to the latest version of the application.")),
|
||||
"This version of the Comunic application is deprecated. You might still be able to use it, but some features may not work. We recommend you to update to the latest version of the application.")!),
|
||||
actions: [
|
||||
MaterialButton(
|
||||
onPressed: () =>
|
||||
launch(ServerConfigurationHelper.config.playStoreURL),
|
||||
child: Text(tr("Go to the Play Store")),
|
||||
launch(ServerConfigurationHelper.config!.playStoreURL),
|
||||
child: Text(tr("Go to the Play Store")!),
|
||||
),
|
||||
MaterialButton(
|
||||
onPressed: () =>
|
||||
launch(ServerConfigurationHelper.config.androidDirectDownloadURL),
|
||||
child: Text(tr("Download update outside Play Store")),
|
||||
launch(ServerConfigurationHelper.config!.androidDirectDownloadURL),
|
||||
child: Text(tr("Download update outside Play Store")!),
|
||||
),
|
||||
MaterialButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
child: Text(tr("Use the old application anyway")),
|
||||
child: Text(tr("Use the old application anyway")!),
|
||||
)
|
||||
],
|
||||
);
|
||||
|
@ -8,9 +8,9 @@ import 'package:flutter/material.dart';
|
||||
/// @author Pierre HUBERT
|
||||
|
||||
/// Ask the user to enter a new password
|
||||
Future<String> showInputNewPassword({
|
||||
@required BuildContext context,
|
||||
@required UserInfoForPassword userInfo,
|
||||
Future<String?> showInputNewPassword({
|
||||
required BuildContext context,
|
||||
required UserInfoForPassword userInfo,
|
||||
}) async {
|
||||
assert(context != null);
|
||||
assert(userInfo != null);
|
||||
@ -25,8 +25,8 @@ class _InputNewPasswordDialog extends StatefulWidget {
|
||||
final UserInfoForPassword userInfo;
|
||||
|
||||
const _InputNewPasswordDialog({
|
||||
Key key,
|
||||
@required this.userInfo,
|
||||
Key? key,
|
||||
required this.userInfo,
|
||||
}) : assert(userInfo != null),
|
||||
super(key: key);
|
||||
|
||||
@ -40,16 +40,16 @@ class __InputNewPasswordDialogState extends State<_InputNewPasswordDialog> {
|
||||
final _controller2 = TextEditingController();
|
||||
final _focusScopeNode = FocusScopeNode();
|
||||
|
||||
String get _password => _controller1.currentState.value;
|
||||
String get _password => _controller1.currentState!.value;
|
||||
|
||||
bool get _input1Valid =>
|
||||
_controller1.currentState != null && _controller1.currentState.valid;
|
||||
_controller1.currentState != null && _controller1.currentState!.valid;
|
||||
|
||||
bool get _input2Valid => _controller1.currentState.value == _controller2.text;
|
||||
bool get _input2Valid => _controller1.currentState!.value == _controller2.text;
|
||||
|
||||
bool get _isValid => _input1Valid && _input2Valid;
|
||||
|
||||
void Function() get _submitAction =>
|
||||
void Function()? get _submitAction =>
|
||||
_isValid ? () => Navigator.of(context).pop(_password) : null;
|
||||
|
||||
@override
|
||||
@ -61,16 +61,16 @@ class __InputNewPasswordDialogState extends State<_InputNewPasswordDialog> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: Text(tr("New password")),
|
||||
title: Text(tr("New password")!),
|
||||
content: AutoSizeDialogContentWidget(child: _buildContent()),
|
||||
actions: <Widget>[
|
||||
MaterialButton(
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
child: Text(tr("Cancel").toUpperCase()),
|
||||
child: Text(tr("Cancel")!.toUpperCase()),
|
||||
),
|
||||
MaterialButton(
|
||||
onPressed: _submitAction,
|
||||
child: Text(tr("Confirm").toUpperCase()),
|
||||
child: Text(tr("Confirm")!.toUpperCase()),
|
||||
),
|
||||
],
|
||||
);
|
||||
@ -85,7 +85,7 @@ class __InputNewPasswordDialogState extends State<_InputNewPasswordDialog> {
|
||||
NewPasswordInputWidget(
|
||||
key: _controller1,
|
||||
user: widget.userInfo,
|
||||
label: tr("Your new password"),
|
||||
label: tr("Your new password")!,
|
||||
textInputAction: TextInputAction.next,
|
||||
onSubmitted: () => _focusScopeNode.nextFocus(),
|
||||
),
|
||||
@ -105,17 +105,17 @@ class __InputNewPasswordDialogState extends State<_InputNewPasswordDialog> {
|
||||
}
|
||||
|
||||
Widget _buildPasswordField({
|
||||
@required TextEditingController controller,
|
||||
@required String label,
|
||||
@required String errorText,
|
||||
@required TextInputAction textInputAction,
|
||||
@required void Function() onSubmitted,
|
||||
required TextEditingController controller,
|
||||
required String? label,
|
||||
required String? errorText,
|
||||
required TextInputAction textInputAction,
|
||||
required void Function()? onSubmitted,
|
||||
}) {
|
||||
return TextFormField(
|
||||
textInputAction: textInputAction,
|
||||
controller: controller,
|
||||
onChanged: (s) => setState(() {}),
|
||||
onFieldSubmitted: (s) => onSubmitted(),
|
||||
onFieldSubmitted: (s) => onSubmitted!(),
|
||||
obscureText: true,
|
||||
decoration: InputDecoration(
|
||||
alignLabelWithHint: true,
|
||||
|
@ -8,20 +8,20 @@ import 'package:flutter/material.dart';
|
||||
/// @author Pierre Hubert
|
||||
|
||||
/// Ask the user to enter an URL
|
||||
Future<String> showInputURLDialog({
|
||||
@required BuildContext context,
|
||||
@required String title,
|
||||
String initialURL,
|
||||
Future<String?> showInputURLDialog({
|
||||
required BuildContext context,
|
||||
required String? title,
|
||||
String? initialURL,
|
||||
}) async {
|
||||
return await showDialog(
|
||||
context: context,
|
||||
builder: (c) => SingleInputDialog(
|
||||
title: title,
|
||||
title: title!,
|
||||
icon: Icons.link,
|
||||
initialValue: initialURL,
|
||||
label: "http://...",
|
||||
checkInput: (s) => validateUrl(s),
|
||||
errorMessage: tr("Invalid URL!"),
|
||||
errorMessage: tr("Invalid URL!")!,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ import 'package:flutter/material.dart';
|
||||
|
||||
enum _Status { NONE, CHECKING, ERROR }
|
||||
|
||||
Future<String> showUserPasswordDialog(BuildContext context) async {
|
||||
Future<String?> showUserPasswordDialog(BuildContext context) async {
|
||||
return await showDialog(
|
||||
context: context, builder: (c) => _InputUserPasswordDialog());
|
||||
}
|
||||
@ -37,16 +37,16 @@ class __InputUserPasswordDialogState
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: Text(tr("Password required")),
|
||||
title: Text(tr("Password required")!),
|
||||
content: _buildContent(),
|
||||
actions: <Widget>[
|
||||
MaterialButton(
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
child: Text(tr("Cancel").toUpperCase()),
|
||||
child: Text(tr("Cancel")!.toUpperCase()),
|
||||
),
|
||||
MaterialButton(
|
||||
onPressed: _canSubmit ? _checkPassword : null,
|
||||
child: Text(tr("Submit").toUpperCase()),
|
||||
child: Text(tr("Submit")!.toUpperCase()),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
@ -7,19 +7,19 @@ import 'package:flutter/material.dart';
|
||||
/// @author Pierre Hubert
|
||||
|
||||
/// Ask the user to input a YouTube ID
|
||||
Future<String> showInputYouTubeIDDialog(
|
||||
BuildContext context, String initialID) async {
|
||||
Future<String?> showInputYouTubeIDDialog(
|
||||
BuildContext context, String? initialID) async {
|
||||
final value = await showDialog<String>(
|
||||
context: context,
|
||||
builder: (b) => SingleInputDialog(
|
||||
title: tr("Input YouTube URL"),
|
||||
title: tr("Input YouTube URL")!,
|
||||
icon: Icons.ondemand_video,
|
||||
initialValue: initialID == null
|
||||
? null
|
||||
: "https://www.youtube.com/watch/?v=" + initialID,
|
||||
label: tr("https://www.youtube.com/watch/?v="),
|
||||
label: tr("https://www.youtube.com/watch/?v=")!,
|
||||
checkInput: (s) => RegExp(r'watch\/\?v=[\w\-\_]+').hasMatch(s),
|
||||
errorMessage: tr("Invalid YouTube link!"),
|
||||
errorMessage: tr("Invalid YouTube link!")!,
|
||||
));
|
||||
|
||||
if (value == null) return null;
|
||||
|
@ -10,12 +10,12 @@ import 'package:flutter/material.dart';
|
||||
class MultiChoiceEntry<T> {
|
||||
final T id;
|
||||
final String title;
|
||||
final String subtitle;
|
||||
final String? subtitle;
|
||||
final bool hidden;
|
||||
|
||||
const MultiChoiceEntry({
|
||||
@required this.id,
|
||||
@required this.title,
|
||||
required this.id,
|
||||
required this.title,
|
||||
this.subtitle,
|
||||
this.hidden = false,
|
||||
}) : assert(id != null),
|
||||
@ -26,16 +26,16 @@ class MultiChoiceEntry<T> {
|
||||
}
|
||||
|
||||
/// Show multiple choices dialog
|
||||
Future<T> showMultiChoicesDialog<T>({
|
||||
@required BuildContext context,
|
||||
@required List<MultiChoiceEntry<T>> choices,
|
||||
@required T defaultChoice,
|
||||
@required String title,
|
||||
Future<T?> showMultiChoicesDialog<T>({
|
||||
required BuildContext context,
|
||||
required List<MultiChoiceEntry<T>> choices,
|
||||
required T defaultChoice,
|
||||
required String? title,
|
||||
}) async {
|
||||
return await showDialog<T>(
|
||||
context: context,
|
||||
builder: (c) => _MultiChoicesEntryDialog(
|
||||
title: title,
|
||||
title: title!,
|
||||
defaultChoice: defaultChoice,
|
||||
choices: choices,
|
||||
));
|
||||
@ -47,10 +47,10 @@ class _MultiChoicesEntryDialog<T> extends StatefulWidget {
|
||||
final List<MultiChoiceEntry<T>> choices;
|
||||
|
||||
const _MultiChoicesEntryDialog({
|
||||
Key key,
|
||||
@required this.title,
|
||||
@required this.defaultChoice,
|
||||
@required this.choices,
|
||||
Key? key,
|
||||
required this.title,
|
||||
required this.defaultChoice,
|
||||
required this.choices,
|
||||
}) : assert(title != null),
|
||||
assert(defaultChoice != null),
|
||||
assert(choices != null),
|
||||
@ -63,7 +63,7 @@ class _MultiChoicesEntryDialog<T> extends StatefulWidget {
|
||||
|
||||
class __MultiChoicesEntryDialogState<T>
|
||||
extends State<_MultiChoicesEntryDialog<T>> {
|
||||
T _currChoice;
|
||||
T? _currChoice;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@ -86,7 +86,7 @@ class __MultiChoicesEntryDialogState<T>
|
||||
onChanged: (v) => setState(() => _currChoice = v),
|
||||
),
|
||||
title: Text(f.title),
|
||||
subtitle: f.hasSubtitle ? Text(f.subtitle) : null,
|
||||
subtitle: f.hasSubtitle ? Text(f.subtitle!) : null,
|
||||
))
|
||||
.toList(),
|
||||
),
|
||||
@ -94,12 +94,12 @@ class __MultiChoicesEntryDialogState<T>
|
||||
actions: <Widget>[
|
||||
MaterialButton(
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
child: Text(tr("Cancel").toUpperCase()),
|
||||
child: Text(tr("Cancel")!.toUpperCase()),
|
||||
textColor: Colors.red,
|
||||
),
|
||||
MaterialButton(
|
||||
onPressed: () => Navigator.of(context).pop(_currChoice),
|
||||
child: Text(tr("Confirm").toUpperCase()),
|
||||
child: Text(tr("Confirm")!.toUpperCase()),
|
||||
textColor: Colors.green,
|
||||
),
|
||||
],
|
||||
|
@ -7,8 +7,8 @@ import 'package:flutter/material.dart';
|
||||
/// @author Pierre Hubert
|
||||
|
||||
/// Show a dialog to prompt information about a new survey to create
|
||||
Future<NewSurvey> showNewSurveyDialog(
|
||||
{@required BuildContext context, NewSurvey initialSurvey}) async {
|
||||
Future<NewSurvey?> showNewSurveyDialog(
|
||||
{required BuildContext context, NewSurvey? initialSurvey}) async {
|
||||
return await showDialog<NewSurvey>(
|
||||
context: context,
|
||||
builder: (c) => _SurveyDialog(
|
||||
@ -17,25 +17,25 @@ Future<NewSurvey> showNewSurveyDialog(
|
||||
}
|
||||
|
||||
class _SurveyDialog extends StatefulWidget {
|
||||
final NewSurvey initialSurvey;
|
||||
final NewSurvey? initialSurvey;
|
||||
|
||||
const _SurveyDialog({Key key, this.initialSurvey}) : super(key: key);
|
||||
const _SurveyDialog({Key? key, this.initialSurvey}) : super(key: key);
|
||||
|
||||
@override
|
||||
__SurveyDialogState createState() => __SurveyDialogState();
|
||||
}
|
||||
|
||||
class __SurveyDialogState extends State<_SurveyDialog> {
|
||||
TextEditingController _questionController;
|
||||
TextEditingController? _questionController;
|
||||
final _choiceController = TextEditingController();
|
||||
var _choices = Set<String>();
|
||||
var _allowNewChoices = false;
|
||||
|
||||
bool get canConfirm =>
|
||||
_questionController.text.length > 2 && _choices.length > 1;
|
||||
_questionController!.text.length > 2 && _choices.length > 1;
|
||||
|
||||
NewSurvey get newSurvey => NewSurvey(
|
||||
question: _questionController.text,
|
||||
question: _questionController!.text,
|
||||
answers: _choices,
|
||||
allowNewChoicesCreation: _allowNewChoices);
|
||||
|
||||
@ -43,31 +43,31 @@ class __SurveyDialogState extends State<_SurveyDialog> {
|
||||
void initState() {
|
||||
super.initState();
|
||||
_questionController = TextEditingController(
|
||||
text: widget.initialSurvey == null ? "" : widget.initialSurvey.question,
|
||||
text: widget.initialSurvey == null ? "" : widget.initialSurvey!.question,
|
||||
);
|
||||
if (widget.initialSurvey != null) {
|
||||
_choices = widget.initialSurvey.answers;
|
||||
_allowNewChoices = widget.initialSurvey.allowNewChoicesCreation;
|
||||
_choices = widget.initialSurvey!.answers;
|
||||
_allowNewChoices = widget.initialSurvey!.allowNewChoicesCreation;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: Text(tr("New survey")),
|
||||
title: Text(tr("New survey")!),
|
||||
content: _buildBody(),
|
||||
actions: <Widget>[
|
||||
// Cancel
|
||||
MaterialButton(
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
child: Text(tr("Cancel").toUpperCase()),
|
||||
child: Text(tr("Cancel")!.toUpperCase()),
|
||||
),
|
||||
|
||||
// Confirm
|
||||
MaterialButton(
|
||||
onPressed:
|
||||
canConfirm ? () => Navigator.of(context).pop(newSurvey) : null,
|
||||
child: Text(tr("Confirm").toUpperCase()),
|
||||
child: Text(tr("Confirm")!.toUpperCase()),
|
||||
),
|
||||
],
|
||||
);
|
||||
@ -90,7 +90,7 @@ class __SurveyDialogState extends State<_SurveyDialog> {
|
||||
),
|
||||
|
||||
Container(height: 30),
|
||||
Text(tr("Current choices:")),
|
||||
Text(tr("Current choices:")!),
|
||||
_buildCurrentChoices(),
|
||||
|
||||
Container(
|
||||
@ -118,7 +118,7 @@ class __SurveyDialogState extends State<_SurveyDialog> {
|
||||
value: _allowNewChoices,
|
||||
onChanged: (v) => setState(() => _allowNewChoices = v),
|
||||
),
|
||||
title: Text(tr("Allow users to create new choices")),
|
||||
title: Text(tr("Allow users to create new choices")!),
|
||||
)
|
||||
]),
|
||||
),
|
||||
@ -126,7 +126,7 @@ class __SurveyDialogState extends State<_SurveyDialog> {
|
||||
}
|
||||
|
||||
Widget _buildCurrentChoices() {
|
||||
if (_choices.length == 0) return Text(tr("No choice yet."));
|
||||
if (_choices.length == 0) return Text(tr("No choice yet.")!);
|
||||
|
||||
return Column(
|
||||
children: _choices
|
||||
|
@ -35,10 +35,10 @@ class _PickFileOption {
|
||||
final _CanEnable canEnable;
|
||||
|
||||
const _PickFileOption({
|
||||
@required this.value,
|
||||
@required this.label,
|
||||
@required this.icon,
|
||||
@required this.canEnable,
|
||||
required this.value,
|
||||
required this.label,
|
||||
required this.icon,
|
||||
required this.canEnable,
|
||||
}) : assert(value != null),
|
||||
assert(label != null),
|
||||
assert(icon != null),
|
||||
@ -49,50 +49,50 @@ List<_PickFileOption> get _optionsList => [
|
||||
// Image
|
||||
_PickFileOption(
|
||||
value: _FileChoices.PICK_IMAGE,
|
||||
label: tr("Choose an image"),
|
||||
label: tr("Choose an image")!,
|
||||
icon: Icons.image,
|
||||
canEnable: (l) => l.any(isImage)),
|
||||
_PickFileOption(
|
||||
value: _FileChoices.TAKE_PICTURE,
|
||||
label: tr("Take a picture"),
|
||||
label: tr("Take a picture")!,
|
||||
icon: Icons.camera_alt,
|
||||
canEnable: (l) => l.any(isImage)),
|
||||
|
||||
// Video
|
||||
_PickFileOption(
|
||||
value: _FileChoices.PICK_VIDEO,
|
||||
label: tr("Choose a video"),
|
||||
label: tr("Choose a video")!,
|
||||
icon: Icons.video_library,
|
||||
canEnable: (l) => l.any(isVideo)),
|
||||
_PickFileOption(
|
||||
value: _FileChoices.TAKE_VIDEO,
|
||||
label: tr("Take a video"),
|
||||
label: tr("Take a video")!,
|
||||
icon: Icons.videocam,
|
||||
canEnable: (l) => l.any(isVideo)),
|
||||
|
||||
// Audio
|
||||
_PickFileOption(
|
||||
value: _FileChoices.RECORD_AUDIO,
|
||||
label: tr("Record audio"),
|
||||
label: tr("Record audio")!,
|
||||
icon: Icons.mic,
|
||||
canEnable: (l) => l.any(isAudio)),
|
||||
|
||||
// Other
|
||||
_PickFileOption(
|
||||
value: _FileChoices.PICK_OTHER_FILE,
|
||||
label: tr("Browse files"),
|
||||
label: tr("Browse files")!,
|
||||
icon: Icons.folder_open,
|
||||
canEnable: (l) =>
|
||||
l.any((el) => !isImage(el) && !isVideo(el) && !isAudio(el))),
|
||||
];
|
||||
|
||||
Future<BytesFile> showPickFileDialog({
|
||||
@required BuildContext context,
|
||||
int maxFileSize,
|
||||
List<String> allowedMimeTypes,
|
||||
int imageMaxWidth,
|
||||
int imageMaxHeight,
|
||||
CropAspectRatio aspectRatio,
|
||||
Future<BytesFile?> showPickFileDialog({
|
||||
required BuildContext context,
|
||||
int? maxFileSize,
|
||||
required List<String> allowedMimeTypes,
|
||||
int? imageMaxWidth,
|
||||
int? imageMaxHeight,
|
||||
CropAspectRatio? aspectRatio,
|
||||
}) async {
|
||||
assert(allowedMimeTypes != null);
|
||||
|
||||
@ -118,7 +118,7 @@ Future<BytesFile> showPickFileDialog({
|
||||
|
||||
if (choice == null) return null;
|
||||
|
||||
BytesFile file;
|
||||
BytesFile? file;
|
||||
switch (choice) {
|
||||
// Pick an image
|
||||
case _FileChoices.PICK_IMAGE:
|
||||
@ -127,8 +127,8 @@ Future<BytesFile> showPickFileDialog({
|
||||
source: choice == _FileChoices.PICK_IMAGE
|
||||
? ImageSource.gallery
|
||||
: ImageSource.camera,
|
||||
maxWidth: imageMaxWidth.toDouble(),
|
||||
maxHeight: imageMaxHeight.toDouble(),
|
||||
maxWidth: imageMaxWidth!.toDouble(),
|
||||
maxHeight: imageMaxHeight!.toDouble(),
|
||||
);
|
||||
|
||||
if (image == null) return null;
|
||||
@ -179,11 +179,11 @@ Future<BytesFile> showPickFileDialog({
|
||||
if (file == null) return null;
|
||||
|
||||
// Check file size
|
||||
if (maxFileSize != null && file.bytes.length > maxFileSize) {
|
||||
if (maxFileSize != null && file.bytes!.length > maxFileSize) {
|
||||
showSimpleSnack(
|
||||
context,
|
||||
tr("This file could not be sent: it is too big! (Max allowed size: %1%)",
|
||||
args: {"1": filesize(file.bytes.length)}));
|
||||
args: {"1": filesize(file.bytes!.length)})!);
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -195,7 +195,7 @@ class _BottomSheetPickOption extends StatelessWidget {
|
||||
final _OnOptionSelected onOptionSelected;
|
||||
|
||||
const _BottomSheetPickOption(
|
||||
{Key key, @required this.options, @required this.onOptionSelected})
|
||||
{Key? key, required this.options, required this.onOptionSelected})
|
||||
: assert(options != null),
|
||||
assert(onOptionSelected != null),
|
||||
super(key: key);
|
||||
|
@ -13,7 +13,7 @@ import 'package:flutter/material.dart';
|
||||
/// Ask the user to pick a user
|
||||
///
|
||||
/// Returns null if no user was selected
|
||||
Future<int> showPickUserDialog(BuildContext context) async {
|
||||
Future<int?> showPickUserDialog(BuildContext context) async {
|
||||
return await showDialog(context: context, builder: (c) => _PickUserDialog());
|
||||
}
|
||||
|
||||
@ -23,19 +23,19 @@ class _PickUserDialog extends StatefulWidget {
|
||||
}
|
||||
|
||||
class __PickUserDialogState extends SafeState<_PickUserDialog> {
|
||||
User _user;
|
||||
User? _user;
|
||||
|
||||
bool get _isValid => _user != null;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: Text(tr("Choose a user")),
|
||||
title: Text(tr("Choose a user")!),
|
||||
content: _buildContent(),
|
||||
actions: <Widget>[
|
||||
CancelDialogButton(),
|
||||
ConfirmDialogButton(
|
||||
value: _isValid ? _user.id : null,
|
||||
value: _isValid ? _user!.id : null,
|
||||
enabled: _isValid,
|
||||
),
|
||||
],
|
||||
@ -52,7 +52,7 @@ class __PickUserDialogState extends SafeState<_PickUserDialog> {
|
||||
children: <Widget>[
|
||||
PickUserWidget(
|
||||
onSelectUser: (u) => setState(() => _user = u),
|
||||
label: tr("Search user..."),
|
||||
label: tr("Search user...")!,
|
||||
onValueChange: (u) => setState(() => _user = null),
|
||||
),
|
||||
],
|
||||
|
@ -9,9 +9,9 @@ import 'package:flutter/material.dart';
|
||||
|
||||
/// Show post visibility level picker and return selected visibility level
|
||||
Future<PostVisibilityLevel> showPostVisibilityPickerDialog({
|
||||
@required BuildContext context,
|
||||
@required PostVisibilityLevel initialLevel,
|
||||
@required bool isGroup,
|
||||
required BuildContext context,
|
||||
required PostVisibilityLevel initialLevel,
|
||||
required bool isGroup,
|
||||
}) async {
|
||||
assert(context != null);
|
||||
assert(initialLevel != null);
|
||||
@ -30,12 +30,12 @@ Future<PostVisibilityLevel> showPostVisibilityPickerDialog({
|
||||
class _PostVisibilityPickerWidget extends StatelessWidget {
|
||||
final bool isGroup;
|
||||
|
||||
const _PostVisibilityPickerWidget({Key key, @required this.isGroup})
|
||||
const _PostVisibilityPickerWidget({Key? key, required this.isGroup})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext c) => AlertDialog(
|
||||
title: Text(tr("Select new post visibility level")),
|
||||
title: Text(tr("Select new post visibility level")!),
|
||||
|
||||
// Show all options
|
||||
content: ConstrainedBox(
|
||||
@ -45,14 +45,14 @@ class _PostVisibilityPickerWidget extends StatelessWidget {
|
||||
// Public
|
||||
PostVisibilityLevelTile(
|
||||
level: PostVisibilityLevel.PUBLIC,
|
||||
title: tr("Public"),
|
||||
title: tr("Public")!,
|
||||
onSelect: (o) => Navigator.pop(c, o),
|
||||
),
|
||||
|
||||
// Friends only (User page only)
|
||||
PostVisibilityLevelTile(
|
||||
level: PostVisibilityLevel.FRIENDS,
|
||||
title: tr("Friends only"),
|
||||
title: tr("Friends only")!,
|
||||
onSelect: (o) => Navigator.pop(c, o),
|
||||
visible: !isGroup,
|
||||
),
|
||||
@ -60,7 +60,7 @@ class _PostVisibilityPickerWidget extends StatelessWidget {
|
||||
// User only (User page only)
|
||||
PostVisibilityLevelTile(
|
||||
level: PostVisibilityLevel.USER,
|
||||
title: tr("Me only"),
|
||||
title: tr("Me only")!,
|
||||
onSelect: (o) => Navigator.pop(c, o),
|
||||
visible: !isGroup,
|
||||
),
|
||||
@ -68,7 +68,7 @@ class _PostVisibilityPickerWidget extends StatelessWidget {
|
||||
// Group members only (Group page only)
|
||||
PostVisibilityLevelTile(
|
||||
level: PostVisibilityLevel.GROUP_MEMBERS,
|
||||
title: tr("Group members only"),
|
||||
title: tr("Group members only")!,
|
||||
onSelect: (o) => Navigator.pop(c, o),
|
||||
visible: isGroup,
|
||||
),
|
||||
@ -80,7 +80,7 @@ class _PostVisibilityPickerWidget extends StatelessWidget {
|
||||
actions: <Widget>[
|
||||
// Cancel
|
||||
TextButton(
|
||||
child: Text(tr("Cancel").toUpperCase()),
|
||||
child: Text(tr("Cancel")!.toUpperCase()),
|
||||
onPressed: () => Navigator.pop(c, null),
|
||||
),
|
||||
],
|
||||
|
@ -18,7 +18,7 @@ import 'package:video_player/video_player.dart';
|
||||
/// @author Pierre Hubert
|
||||
|
||||
/// Record audio
|
||||
Future<Uint8List> showRecordAudioDialog(BuildContext context) async {
|
||||
Future<Uint8List?> showRecordAudioDialog(BuildContext context) async {
|
||||
// Request record permission
|
||||
if (!await requestPermission(context, Permission.microphone)) {
|
||||
alert(context, tr("Did not get permission to access microphone!"));
|
||||
@ -43,15 +43,15 @@ class _RecordAudioDialog extends StatefulWidget {
|
||||
}
|
||||
|
||||
class __RecordAudioDialogState extends State<_RecordAudioDialog> {
|
||||
String _recordPath;
|
||||
String? _recordPath;
|
||||
|
||||
File get _recordFile => _recordPath == null ? null : File(_recordPath);
|
||||
File? get _recordFile => _recordPath == null ? null : File(_recordPath!);
|
||||
|
||||
bool _recording = false;
|
||||
|
||||
bool get _hasRecord => !_recording && _recordPath != null;
|
||||
|
||||
VideoPlayerController _videoPlayerController;
|
||||
VideoPlayerController? _videoPlayerController;
|
||||
|
||||
bool _playing = false;
|
||||
|
||||
@ -59,8 +59,8 @@ class __RecordAudioDialogState extends State<_RecordAudioDialog> {
|
||||
|
||||
/// Get record data. This getter can be accessed only once
|
||||
Uint8List get _bytes {
|
||||
final bytes = _recordFile.readAsBytesSync();
|
||||
File(_recordPath).deleteSync();
|
||||
final bytes = _recordFile!.readAsBytesSync();
|
||||
File(_recordPath!).deleteSync();
|
||||
return bytes;
|
||||
}
|
||||
|
||||
@ -73,7 +73,7 @@ class __RecordAudioDialogState extends State<_RecordAudioDialog> {
|
||||
|
||||
void _disposePlayer() {
|
||||
if (_videoPlayerController != null) {
|
||||
_videoPlayerController.dispose();
|
||||
_videoPlayerController!.dispose();
|
||||
}
|
||||
_videoPlayerController = null;
|
||||
}
|
||||
@ -81,24 +81,24 @@ class __RecordAudioDialogState extends State<_RecordAudioDialog> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: Text(tr("Audio record")),
|
||||
title: Text(tr("Audio record")!),
|
||||
content: _buildContent(),
|
||||
actions: <Widget>[
|
||||
_ActionButton(
|
||||
visible: !_recording,
|
||||
text: tr("Cancel"),
|
||||
text: tr("Cancel")!,
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
),
|
||||
_ActionButton(
|
||||
visible: _hasRecord,
|
||||
text: tr("Send"),
|
||||
text: tr("Send")!,
|
||||
onPressed: () => Navigator.of(context).pop(_bytes),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
String get _status {
|
||||
String? get _status {
|
||||
if (_recording)
|
||||
return tr("Recording...");
|
||||
else if (_paused)
|
||||
@ -113,7 +113,7 @@ class __RecordAudioDialogState extends State<_RecordAudioDialog> {
|
||||
|
||||
Widget _buildContent() => Row(
|
||||
children: <Widget>[
|
||||
Text(_status),
|
||||
Text(_status!),
|
||||
|
||||
Spacer(),
|
||||
|
||||
@ -165,21 +165,21 @@ class __RecordAudioDialogState extends State<_RecordAudioDialog> {
|
||||
|
||||
void _startRecording() async {
|
||||
try {
|
||||
if (_recordFile != null) _recordFile.deleteSync();
|
||||
if (_recordFile != null) _recordFile!.deleteSync();
|
||||
|
||||
final dir = await getTemporaryDirectory();
|
||||
|
||||
_recordPath = path.join(dir.absolute.path, "tmp-audio-record.mp3");
|
||||
|
||||
RecordMp3.instance.start(_recordPath, (fail) {
|
||||
RecordMp3.instance.start(_recordPath!, (fail) {
|
||||
print(fail);
|
||||
snack(context, tr("Failed to start recording!"));
|
||||
snack(context, tr("Failed to start recording!")!);
|
||||
});
|
||||
|
||||
setState(() => _recording = true);
|
||||
} catch (e, s) {
|
||||
logError(e, s);
|
||||
snack(context, tr("Error while recording!"));
|
||||
snack(context, tr("Error while recording!")!);
|
||||
}
|
||||
}
|
||||
|
||||
@ -190,7 +190,7 @@ class __RecordAudioDialogState extends State<_RecordAudioDialog> {
|
||||
setState(() => _recording = false);
|
||||
} catch (e, s) {
|
||||
logError(e, s);
|
||||
snack(context, tr("Error while recording!"));
|
||||
snack(context, tr("Error while recording!")!);
|
||||
}
|
||||
}
|
||||
|
||||
@ -198,17 +198,17 @@ class __RecordAudioDialogState extends State<_RecordAudioDialog> {
|
||||
try {
|
||||
_disposePlayer();
|
||||
|
||||
_videoPlayerController = VideoPlayerController.file(File(_recordPath));
|
||||
await _videoPlayerController.initialize();
|
||||
_videoPlayerController = VideoPlayerController.file(File(_recordPath!));
|
||||
await _videoPlayerController!.initialize();
|
||||
|
||||
_videoPlayerController.addListener(() async {
|
||||
_videoPlayerController!.addListener(() async {
|
||||
if (_videoPlayerController == null) return;
|
||||
|
||||
if (_videoPlayerController.value.position ==
|
||||
_videoPlayerController.value.duration) _stopPlayback();
|
||||
if (_videoPlayerController!.value.position ==
|
||||
_videoPlayerController!.value.duration) _stopPlayback();
|
||||
});
|
||||
|
||||
await _videoPlayerController.play();
|
||||
await _videoPlayerController!.play();
|
||||
|
||||
setState(() {
|
||||
_playing = true;
|
||||
@ -216,27 +216,27 @@ class __RecordAudioDialogState extends State<_RecordAudioDialog> {
|
||||
});
|
||||
} catch (e, s) {
|
||||
logError(e, s);
|
||||
snack(context, tr("Error while playing record!"));
|
||||
snack(context, tr("Error while playing record!")!);
|
||||
}
|
||||
}
|
||||
|
||||
void _pausePlayback() async {
|
||||
try {
|
||||
await _videoPlayerController.pause();
|
||||
await _videoPlayerController!.pause();
|
||||
setState(() => _paused = true);
|
||||
} catch (e, s) {
|
||||
logError(e, s);
|
||||
snack(context, tr("Error while pausing playback!"));
|
||||
snack(context, tr("Error while pausing playback!")!);
|
||||
}
|
||||
}
|
||||
|
||||
void _resumePlayback() async {
|
||||
try {
|
||||
await _videoPlayerController.play();
|
||||
await _videoPlayerController!.play();
|
||||
setState(() => _paused = false);
|
||||
} catch (e, s) {
|
||||
logError(e, s);
|
||||
snack(context, tr("Error while resuming playback!"));
|
||||
snack(context, tr("Error while resuming playback!")!);
|
||||
}
|
||||
}
|
||||
|
||||
@ -249,7 +249,7 @@ class __RecordAudioDialogState extends State<_RecordAudioDialog> {
|
||||
});
|
||||
} catch (e, s) {
|
||||
logError(e, s);
|
||||
snack(context, tr("Error while stopping playback!"));
|
||||
snack(context, tr("Error while stopping playback!")!);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -258,13 +258,13 @@ class _RecordAction extends StatelessWidget {
|
||||
final bool visible;
|
||||
final IconData icon;
|
||||
final void Function() onTap;
|
||||
final Color color;
|
||||
final Color? color;
|
||||
|
||||
const _RecordAction({
|
||||
Key key,
|
||||
@required this.visible,
|
||||
@required this.icon,
|
||||
@required this.onTap,
|
||||
Key? key,
|
||||
required this.visible,
|
||||
required this.icon,
|
||||
required this.onTap,
|
||||
this.color,
|
||||
}) : assert(visible != null),
|
||||
assert(icon != null),
|
||||
@ -280,12 +280,12 @@ class _RecordAction extends StatelessWidget {
|
||||
class _ActionButton extends StatelessWidget {
|
||||
final bool visible;
|
||||
final String text;
|
||||
final void Function() onPressed;
|
||||
final void Function()? onPressed;
|
||||
|
||||
const _ActionButton({
|
||||
Key key,
|
||||
this.visible,
|
||||
this.text,
|
||||
Key? key,
|
||||
required this.visible,
|
||||
required this.text,
|
||||
this.onPressed,
|
||||
}) : assert(visible != null),
|
||||
assert(text != null),
|
||||
|
@ -11,7 +11,7 @@ import 'package:flutter/material.dart';
|
||||
///
|
||||
/// This widget automatically adapt himself if we are in tablet
|
||||
/// or in mobile mode
|
||||
Future<T> showScreenDialog<T>(BuildContext context, Widget screen) async {
|
||||
Future<T?> showScreenDialog<T>(BuildContext context, Widget screen) async {
|
||||
// TODO : add mobile support
|
||||
if (!isTablet(context)) throw Exception("Unsupported mode!");
|
||||
|
||||
@ -20,9 +20,9 @@ Future<T> showScreenDialog<T>(BuildContext context, Widget screen) async {
|
||||
}
|
||||
|
||||
class _ScreenDialog extends StatelessWidget {
|
||||
final Widget body;
|
||||
final Widget? body;
|
||||
|
||||
const _ScreenDialog({Key key, this.body}) : super(key: key);
|
||||
const _ScreenDialog({Key? key, this.body}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
@ -7,23 +7,23 @@ import 'package:flutter/material.dart';
|
||||
|
||||
class SingleInputDialog extends StatefulWidget {
|
||||
final String title;
|
||||
final IconData icon;
|
||||
final String initialValue;
|
||||
final IconData? icon;
|
||||
final String? initialValue;
|
||||
final String label;
|
||||
final bool Function(String) checkInput;
|
||||
final String errorMessage;
|
||||
final bool canBeEmpty;
|
||||
final int maxLines;
|
||||
final int maxLength;
|
||||
final int? maxLength;
|
||||
|
||||
const SingleInputDialog({
|
||||
Key key,
|
||||
@required this.title,
|
||||
@required this.icon,
|
||||
@required this.initialValue,
|
||||
@required this.label,
|
||||
@required this.checkInput,
|
||||
@required this.errorMessage,
|
||||
Key? key,
|
||||
required this.title,
|
||||
required this.icon,
|
||||
required this.initialValue,
|
||||
required this.label,
|
||||
required this.checkInput,
|
||||
required this.errorMessage,
|
||||
this.canBeEmpty = false,
|
||||
this.maxLines = 1,
|
||||
this.maxLength,
|
||||
@ -39,11 +39,11 @@ class SingleInputDialog extends StatefulWidget {
|
||||
}
|
||||
|
||||
class __InputURLDialogState extends State<SingleInputDialog> {
|
||||
TextEditingController _controller;
|
||||
TextEditingController? _controller;
|
||||
|
||||
bool get _isValid =>
|
||||
(_controller.text.isEmpty && widget.canBeEmpty) ||
|
||||
(_controller.text.isNotEmpty && widget.checkInput(_controller.text));
|
||||
(_controller!.text.isEmpty && widget.canBeEmpty) ||
|
||||
(_controller!.text.isNotEmpty && widget.checkInput(_controller!.text));
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@ -64,7 +64,7 @@ class __InputURLDialogState extends State<SingleInputDialog> {
|
||||
icon: widget.icon == null ? null : Icon(widget.icon),
|
||||
alignLabelWithHint: true,
|
||||
labelText: widget.label,
|
||||
errorText: _controller.text.isNotEmpty && !_isValid
|
||||
errorText: _controller!.text.isNotEmpty && !_isValid
|
||||
? widget.errorMessage
|
||||
: null,
|
||||
),
|
||||
@ -72,16 +72,16 @@ class __InputURLDialogState extends State<SingleInputDialog> {
|
||||
actions: <Widget>[
|
||||
// Cancel
|
||||
MaterialButton(
|
||||
child: Text(tr("Cancel").toUpperCase()),
|
||||
child: Text(tr("Cancel")!.toUpperCase()),
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
textColor: Colors.red,
|
||||
),
|
||||
|
||||
// Confirm
|
||||
MaterialButton(
|
||||
child: Text(tr("Confirm").toUpperCase()),
|
||||
child: Text(tr("Confirm")!.toUpperCase()),
|
||||
onPressed: _isValid
|
||||
? () => Navigator.of(context).pop(_controller.text)
|
||||
? () => Navigator.of(context).pop(_controller!.text)
|
||||
: null,
|
||||
),
|
||||
],
|
||||
|
@ -12,11 +12,11 @@ enum VirtualDirectoryTargetType { USER, GROUP }
|
||||
|
||||
enum _CheckStatus { EMPTY, PENDING, VALID, INVALID }
|
||||
|
||||
Future<String> showVirtualDirectoryDialog({
|
||||
@required BuildContext context,
|
||||
@required String initialDirectory,
|
||||
@required int id,
|
||||
@required VirtualDirectoryTargetType type,
|
||||
Future<String?> showVirtualDirectoryDialog({
|
||||
required BuildContext context,
|
||||
required String initialDirectory,
|
||||
required int id,
|
||||
required VirtualDirectoryTargetType type,
|
||||
}) async {
|
||||
assert(context != null);
|
||||
assert(initialDirectory != null);
|
||||
@ -35,10 +35,10 @@ class _VirtualDirectoryPicker extends StatefulWidget {
|
||||
final VirtualDirectoryTargetType type;
|
||||
|
||||
const _VirtualDirectoryPicker({
|
||||
Key key,
|
||||
@required this.initialDirectory,
|
||||
@required this.id,
|
||||
@required this.type,
|
||||
Key? key,
|
||||
required this.initialDirectory,
|
||||
required this.id,
|
||||
required this.type,
|
||||
}) : assert(initialDirectory != null),
|
||||
assert(id != null),
|
||||
assert(type != null),
|
||||
@ -50,10 +50,10 @@ class _VirtualDirectoryPicker extends StatefulWidget {
|
||||
}
|
||||
|
||||
class __VirtualDirectoryPickerState extends SafeState<_VirtualDirectoryPicker> {
|
||||
TextEditingController _controller;
|
||||
TextEditingController? _controller;
|
||||
var _status = _CheckStatus.VALID;
|
||||
|
||||
String get _currentValue => _controller.text;
|
||||
String get _currentValue => _controller!.text;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@ -64,7 +64,7 @@ class __VirtualDirectoryPickerState extends SafeState<_VirtualDirectoryPicker> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: Text(tr("Choose a virtual directory")),
|
||||
title: Text(tr("Choose a virtual directory")!),
|
||||
|
||||
// Dialog content
|
||||
content: TextField(
|
||||
@ -89,7 +89,7 @@ class __VirtualDirectoryPickerState extends SafeState<_VirtualDirectoryPicker> {
|
||||
// Cancel
|
||||
MaterialButton(
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
child: Text(tr("Cancel").toUpperCase()),
|
||||
child: Text(tr("Cancel")!.toUpperCase()),
|
||||
),
|
||||
|
||||
// Confirm
|
||||
@ -98,7 +98,7 @@ class __VirtualDirectoryPickerState extends SafeState<_VirtualDirectoryPicker> {
|
||||
_status == _CheckStatus.VALID || _status == _CheckStatus.EMPTY
|
||||
? () => Navigator.of(context).pop(_currentValue)
|
||||
: null,
|
||||
child: Text(tr("Confirm").toUpperCase()),
|
||||
child: Text(tr("Confirm")!.toUpperCase()),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
@ -18,9 +18,9 @@ class ConversationMessageStatsRoute extends StatefulWidget {
|
||||
final ConversationMessage message;
|
||||
|
||||
const ConversationMessageStatsRoute({
|
||||
Key key,
|
||||
@required this.conv,
|
||||
@required this.message,
|
||||
Key? key,
|
||||
required this.conv,
|
||||
required this.message,
|
||||
}) : assert(conv != null),
|
||||
assert(message != null),
|
||||
super(key: key);
|
||||
@ -32,7 +32,7 @@ class ConversationMessageStatsRoute extends StatefulWidget {
|
||||
|
||||
class _ConversationMessageStatsRouteState
|
||||
extends State<ConversationMessageStatsRoute> {
|
||||
UsersList _users;
|
||||
late UsersList _users;
|
||||
|
||||
Future<void> _init() async {
|
||||
_users = await UsersHelper()
|
||||
@ -43,23 +43,23 @@ class _ConversationMessageStatsRouteState
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(tr("Message statistics")),
|
||||
title: Text(tr("Message statistics")!),
|
||||
leading: IconButton(
|
||||
icon: Icon(Icons.close),
|
||||
onPressed: () => MainController.of(context).popPage(),
|
||||
onPressed: () => MainController.of(context)!.popPage(),
|
||||
),
|
||||
),
|
||||
body: AsyncScreenWidget(
|
||||
onReload: _init,
|
||||
onBuild: _buildScreen,
|
||||
errorMessage: tr("Failed to load message information!")),
|
||||
errorMessage: tr("Failed to load message information!")!),
|
||||
);
|
||||
}
|
||||
|
||||
List<Widget> get _firstItems => [
|
||||
ListTile(
|
||||
leading: Icon(Icons.access_time_rounded),
|
||||
title: Text(tr("Created on")),
|
||||
title: Text(tr("Created on")!),
|
||||
subtitle: Text(dateTimeToString(widget.message.date)),
|
||||
),
|
||||
ListTile(
|
||||
@ -67,17 +67,17 @@ class _ConversationMessageStatsRouteState
|
||||
user: _users.getUser(widget.message.userID),
|
||||
),
|
||||
title: Text(_users.getUser(widget.message.userID).fullName),
|
||||
subtitle: Text(tr("Creator")),
|
||||
subtitle: Text(tr("Creator")!),
|
||||
),
|
||||
];
|
||||
|
||||
Widget _buildScreen() => ListView.builder(
|
||||
itemCount: _firstItems.length + widget.conv.members.length,
|
||||
itemCount: _firstItems.length + widget.conv.members!.length,
|
||||
itemBuilder: (c, i) {
|
||||
final firstItems = _firstItems;
|
||||
if (i < firstItems.length) return firstItems[i];
|
||||
|
||||
final convMember = widget.conv.members[i - firstItems.length];
|
||||
final convMember = widget.conv.members![i - firstItems.length];
|
||||
|
||||
if (convMember.userID == widget.message.userID) return Container();
|
||||
|
||||
@ -86,9 +86,9 @@ class _ConversationMessageStatsRouteState
|
||||
user: _users.getUser(convMember.userID),
|
||||
),
|
||||
title: Text(_users.getUser(convMember.userID).fullName),
|
||||
subtitle: Text(convMember.lastMessageSeen < widget.message.id
|
||||
? tr("Message not seen yet")
|
||||
: tr("Message seen")),
|
||||
subtitle: Text(convMember.lastMessageSeen < widget.message.id!
|
||||
? tr("Message not seen yet")!
|
||||
: tr("Message seen")!),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
@ -19,8 +19,8 @@ class ConversationRoute extends StatefulWidget {
|
||||
final int conversationID;
|
||||
|
||||
const ConversationRoute({
|
||||
Key key,
|
||||
@required this.conversationID,
|
||||
Key? key,
|
||||
required this.conversationID,
|
||||
}) : assert(conversationID != null),
|
||||
super(key: key);
|
||||
|
||||
@ -30,9 +30,9 @@ class ConversationRoute extends StatefulWidget {
|
||||
|
||||
class _ConversationRouteState extends SafeState<ConversationRoute> {
|
||||
final ConversationsHelper _conversationsHelper = ConversationsHelper();
|
||||
Conversation _conversation;
|
||||
UsersList _users;
|
||||
String _conversationName;
|
||||
Conversation? _conversation;
|
||||
UsersList? _users;
|
||||
String? _conversationName;
|
||||
bool _error = false;
|
||||
|
||||
setError(bool err) => setState(() => _error = err);
|
||||
@ -50,10 +50,10 @@ class _ConversationRouteState extends SafeState<ConversationRoute> {
|
||||
_conversation = await _conversationsHelper
|
||||
.getSingle(widget.conversationID, force: true);
|
||||
|
||||
_users = await UsersHelper().getList(_conversation.membersID);
|
||||
_users = await UsersHelper().getList(_conversation!.membersID);
|
||||
|
||||
final conversationName =
|
||||
ConversationsHelper.getConversationName(_conversation, _users);
|
||||
ConversationsHelper.getConversationName(_conversation!, _users);
|
||||
|
||||
if (!this.mounted) return null;
|
||||
|
||||
@ -65,7 +65,7 @@ class _ConversationRouteState extends SafeState<ConversationRoute> {
|
||||
}
|
||||
|
||||
void _openSettings() =>
|
||||
MainController.of(context).openConversationSettingsRoute(_conversation);
|
||||
MainController.of(context)!.openConversationSettingsRoute(_conversation!);
|
||||
|
||||
Widget _buildRouteBody() {
|
||||
//Handle errors
|
||||
@ -76,7 +76,7 @@ class _ConversationRouteState extends SafeState<ConversationRoute> {
|
||||
TextButton(
|
||||
onPressed: _loadConversation,
|
||||
child: Text(
|
||||
tr("Try again").toUpperCase(),
|
||||
tr("Try again")!.toUpperCase(),
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
),
|
||||
@ -101,19 +101,19 @@ class _ConversationRouteState extends SafeState<ConversationRoute> {
|
||||
? (_conversation == null || _users == null
|
||||
? null
|
||||
: ConversationImageWidget(
|
||||
conversation: _conversation, users: _users))
|
||||
conversation: _conversation!, users: _users!))
|
||||
: ComunicBackButton(),
|
||||
title: Text(
|
||||
_conversationName == null ? tr("Loading") : _conversationName,
|
||||
_conversationName == null ? tr("Loading")! : _conversationName!,
|
||||
),
|
||||
actions: <Widget>[
|
||||
// Start call (if possible)
|
||||
_conversation == null ||
|
||||
_conversation.callCapabilities == CallCapabilities.NONE
|
||||
_conversation!.callCapabilities == CallCapabilities.NONE
|
||||
? Container()
|
||||
: IconButton(
|
||||
icon: Icon(Icons.phone),
|
||||
onPressed: () => MainController.of(context)
|
||||
onPressed: () => MainController.of(context)!
|
||||
.startCall(widget.conversationID),
|
||||
),
|
||||
|
||||
|
@ -15,7 +15,7 @@ class CreateAccountRoute extends StatelessWidget {
|
||||
return LoginRouteContainer(
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(tr("Create an account")),
|
||||
title: Text(tr("Create an account")!),
|
||||
),
|
||||
body: _CreateAccountRouteBody(),
|
||||
),
|
||||
@ -38,12 +38,12 @@ class __CreateAccountRouteBodyState extends State<_CreateAccountRouteBody> {
|
||||
await showCupertinoDialog(
|
||||
context: context,
|
||||
builder: (c) => CupertinoAlertDialog(
|
||||
title: Text(tr("Account created")),
|
||||
title: Text(tr("Account created")!),
|
||||
content: Text(tr(
|
||||
"Your account has been successfully created. You can now login to start to use it.")),
|
||||
"Your account has been successfully created. You can now login to start to use it.")!),
|
||||
actions: <Widget>[
|
||||
CupertinoButton(
|
||||
child: Text(tr("Login")), onPressed: () => Navigator.of(c).pop())
|
||||
child: Text(tr("Login")!), onPressed: () => Navigator.of(c).pop())
|
||||
],
|
||||
),
|
||||
);
|
||||
|
@ -16,16 +16,16 @@ import 'package:flutter/material.dart';
|
||||
|
||||
/// Show presence settings route
|
||||
Future<void> showPresenceSettingsRoute(
|
||||
BuildContext context, int groupID) async =>
|
||||
BuildContext context, int? groupID) async =>
|
||||
await Navigator.push(context,
|
||||
MaterialPageRoute(builder: (c) => PresenceSettings(groupID: groupID)));
|
||||
MaterialPageRoute(builder: (c) => PresenceSettings(groupID: groupID!)));
|
||||
|
||||
class PresenceSettings extends StatefulWidget {
|
||||
final int groupID;
|
||||
|
||||
const PresenceSettings({
|
||||
Key key,
|
||||
@required this.groupID,
|
||||
Key? key,
|
||||
required this.groupID,
|
||||
}) : assert(groupID != null),
|
||||
super(key: key);
|
||||
|
||||
@ -34,7 +34,7 @@ class PresenceSettings extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _PresenceSettingsState extends State<PresenceSettings> {
|
||||
PresenceSet _currPresences;
|
||||
late PresenceSet _currPresences;
|
||||
|
||||
Future<void> _load() async {
|
||||
await ForezPresenceHelper.refreshCache(widget.groupID);
|
||||
@ -46,12 +46,12 @@ class _PresenceSettingsState extends State<PresenceSettings> {
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(tr("Presence settings")),
|
||||
title: Text(tr("Presence settings")!),
|
||||
),
|
||||
body: AsyncScreenWidget(
|
||||
onReload: _load,
|
||||
onBuild: _buildScreen,
|
||||
errorMessage: tr("Failed to load data!"),
|
||||
errorMessage: tr("Failed to load data!")!,
|
||||
),
|
||||
);
|
||||
}
|
||||
@ -61,7 +61,7 @@ class _PresenceSettingsState extends State<PresenceSettings> {
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(
|
||||
tr("Please click on the day you will be in the plain, so that everyone gets informed ! ;)"),
|
||||
tr("Please click on the day you will be in the plain, so that everyone gets informed ! ;)")!,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
@ -85,7 +85,7 @@ class _PresenceSettingsState extends State<PresenceSettings> {
|
||||
} catch (e, s) {
|
||||
logError(e, s);
|
||||
_currPresences.toggleDate(dt, userID());
|
||||
snack(context, tr("Failed to update information!"));
|
||||
snack(context, tr("Failed to update information!")!);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ class ForgotPasswordRoute extends StatelessWidget {
|
||||
return LoginRouteContainer(
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(tr("Password forgotten")),
|
||||
title: Text(tr("Password forgotten")!),
|
||||
),
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
@ -45,7 +45,7 @@ class _ResetPasswordBodyState extends SafeState<_ResetPasswordBody> {
|
||||
var _loading = false;
|
||||
|
||||
/// Step 1 - check email address
|
||||
String _emailAddress;
|
||||
String? _emailAddress;
|
||||
final _emailController = TextEditingController();
|
||||
|
||||
String get _inputEmail => _emailController.text;
|
||||
@ -54,14 +54,14 @@ class _ResetPasswordBodyState extends SafeState<_ResetPasswordBody> {
|
||||
_inputEmail.isNotEmpty && validateEmail(_inputEmail);
|
||||
|
||||
/// Step 2 - Offer options
|
||||
bool _hasSecurityQuestions;
|
||||
bool? _hasSecurityQuestions;
|
||||
|
||||
var _selectedOption = _SelectedOption.NONE;
|
||||
|
||||
void _setLoading(bool loading) => setState(() => _loading = loading);
|
||||
|
||||
/// Step 3b - Answer security questions
|
||||
List<String> _questions;
|
||||
List<String>? _questions;
|
||||
var _questionsControllers = <TextEditingController>[];
|
||||
|
||||
List<String> get _answers =>
|
||||
@ -90,7 +90,7 @@ class _ResetPasswordBodyState extends SafeState<_ResetPasswordBody> {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Text(tr("Please enter your email address to reset your password:")),
|
||||
Text(tr("Please enter your email address to reset your password:")!),
|
||||
TextField(
|
||||
controller: _emailController,
|
||||
onChanged: (s) => setState(() {}),
|
||||
@ -120,9 +120,9 @@ class _ResetPasswordBodyState extends SafeState<_ResetPasswordBody> {
|
||||
_setLoading(true);
|
||||
|
||||
// Check if email address exists or not
|
||||
if (!await AccountHelper.existsMailAccount(_inputEmail)) {
|
||||
if (!await AccountHelper.existsMailAccount(_inputEmail) ) {
|
||||
_setLoading(false);
|
||||
showSimpleSnack(context, tr("Specified email address was not found!"));
|
||||
showSimpleSnack(context, tr("Specified email address was not found!")!);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -135,7 +135,7 @@ class _ResetPasswordBodyState extends SafeState<_ResetPasswordBody> {
|
||||
} catch (e, s) {
|
||||
print("Could not check given email! $e\n$s");
|
||||
showSimpleSnack(context,
|
||||
tr("An error occurred while checking your recovery options !"));
|
||||
tr("An error occurred while checking your recovery options !")!);
|
||||
_setLoading(false);
|
||||
}
|
||||
}
|
||||
@ -145,19 +145,19 @@ class _ResetPasswordBodyState extends SafeState<_ResetPasswordBody> {
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: <Widget>[
|
||||
Text(tr("Here are your options to reset your account:")),
|
||||
Text(tr("Here are your options to reset your account:")!),
|
||||
_Spacer(),
|
||||
OutlinedButton.icon(
|
||||
onPressed: _openSendEmailDialog,
|
||||
icon: Icon(Icons.email),
|
||||
label: Flexible(
|
||||
child: Text(tr("Send us an email to ask for help")))),
|
||||
_Spacer(visible: _hasSecurityQuestions),
|
||||
_hasSecurityQuestions
|
||||
child: Text(tr("Send us an email to ask for help")!))),
|
||||
_Spacer(visible: _hasSecurityQuestions!),
|
||||
_hasSecurityQuestions!
|
||||
? OutlinedButton.icon(
|
||||
onPressed: _loadSecurityQuestions,
|
||||
icon: Icon(Icons.help_outline),
|
||||
label: Text(tr("Answer your security questions")),
|
||||
label: Text(tr("Answer your security questions")!),
|
||||
)
|
||||
: Container(),
|
||||
],
|
||||
@ -169,7 +169,7 @@ class _ResetPasswordBodyState extends SafeState<_ResetPasswordBody> {
|
||||
context: context,
|
||||
builder: (c) => AlertDialog(
|
||||
title: Text("Contact us"),
|
||||
content: Text(tr("You can reach us at contact@communiquons.org")),
|
||||
content: Text(tr("You can reach us at contact@communiquons.org")!),
|
||||
actions: <Widget>[CancelDialogButton()],
|
||||
));
|
||||
}
|
||||
@ -181,11 +181,11 @@ class _ResetPasswordBodyState extends SafeState<_ResetPasswordBody> {
|
||||
_questions = await AccountHelper.getSecurityQuestions(_emailAddress);
|
||||
|
||||
_questionsControllers =
|
||||
List.generate(_questions.length, (i) => TextEditingController());
|
||||
List.generate(_questions!.length, (i) => TextEditingController());
|
||||
_selectedOption = _SelectedOption.SECURITY_QUESTIONS;
|
||||
} catch (e, s) {
|
||||
print("Could not load security questions! $e\n$s");
|
||||
showSimpleSnack(context, tr("Could not load your security questions!"));
|
||||
showSimpleSnack(context, tr("Could not load your security questions!")!);
|
||||
}
|
||||
_setLoading(false);
|
||||
}
|
||||
@ -196,20 +196,20 @@ class _ResetPasswordBodyState extends SafeState<_ResetPasswordBody> {
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: <Widget>[]
|
||||
..add(Text(tr("Please answer now your security questions:")))
|
||||
..add(Text(tr("Please answer now your security questions:")!))
|
||||
..add(_Spacer())
|
||||
..addAll(List.generate(_questions.length, _buildSecurityQuestionField))
|
||||
..addAll(List.generate(_questions!.length, _buildSecurityQuestionField))
|
||||
..add(_Spacer())
|
||||
..add(OutlinedButton(
|
||||
onPressed: _canSubmitAnswers ? _submitSecurityAnswers : null,
|
||||
child: Text(tr("Submit")),
|
||||
child: Text(tr("Submit")!),
|
||||
)),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSecurityQuestionField(int id) => Column(
|
||||
children: <Widget>[
|
||||
Text(_questions[id]),
|
||||
Text(_questions![id]),
|
||||
TextField(
|
||||
controller: _questionsControllers[id],
|
||||
onChanged: (s) => setState(() {}),
|
||||
@ -234,20 +234,20 @@ class _ResetPasswordBodyState extends SafeState<_ResetPasswordBody> {
|
||||
} catch (e, s) {
|
||||
print("Could not submit security answers! $e\n$s");
|
||||
showSimpleSnack(
|
||||
context, tr("Could not validate these security answers!"));
|
||||
context, tr("Could not validate these security answers!")!);
|
||||
}
|
||||
_setLoading(false);
|
||||
}
|
||||
|
||||
/// Call this method whenever the user managed to get a password reset token
|
||||
void _useResetToken(String token) => Navigator.of(context).pushReplacement(
|
||||
MaterialPageRoute(builder: (c) => PasswordResetRoute(token: token)));
|
||||
void _useResetToken(String? token) => Navigator.of(context).pushReplacement(
|
||||
MaterialPageRoute(builder: (c) => PasswordResetRoute(token: token!)));
|
||||
}
|
||||
|
||||
class _Spacer extends StatelessWidget {
|
||||
final bool visible;
|
||||
|
||||
const _Spacer({Key key, this.visible = true})
|
||||
const _Spacer({Key? key, this.visible = true})
|
||||
: assert(visible != null),
|
||||
super(key: key);
|
||||
|
||||
|
@ -22,7 +22,7 @@ class _FullScreenImageRouteState extends State<FullScreenImageRoute> {
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(tr("Image")),
|
||||
title: Text(tr("Image")!),
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: Icon(Icons.launch), onPressed: () => launch(widget.url))
|
||||
|
@ -18,18 +18,16 @@ import '../../utils/files_utils.dart';
|
||||
///
|
||||
/// Return original image in case of error / if the user did not crop the image
|
||||
Future<BytesFile> showImageCropper(BuildContext context, BytesFile source,
|
||||
{CropAspectRatio aspectRatio}) async {
|
||||
assert(context != null);
|
||||
assert(source != null);
|
||||
{CropAspectRatio? aspectRatio}) async {
|
||||
|
||||
File file;
|
||||
File cropped;
|
||||
File? file;
|
||||
File? cropped;
|
||||
|
||||
try {
|
||||
file = await generateTemporaryFile();
|
||||
await file.writeAsBytes(source.bytes);
|
||||
await file.writeAsBytes(source.bytes!);
|
||||
|
||||
File cropped = await ImageCropper().cropImage(
|
||||
File? cropped = await ImageCropper().cropImage(
|
||||
sourcePath: file.absolute.path,
|
||||
compressFormat: ImageCompressFormat.png,
|
||||
aspectRatio: aspectRatio,
|
||||
@ -40,12 +38,12 @@ Future<BytesFile> showImageCropper(BuildContext context, BytesFile source,
|
||||
),
|
||||
);
|
||||
|
||||
if (cropped == null) return null;
|
||||
if (cropped == null) return source;
|
||||
|
||||
return BytesFile("cropped.png", await cropped.readAsBytes());
|
||||
} catch (e, s) {
|
||||
logError(e, s);
|
||||
snack(context, tr("Failed to execute image cropper!"));
|
||||
snack(context, tr("Failed to execute image cropper!")!);
|
||||
return source;
|
||||
} finally {
|
||||
if (file != null && await file.exists()) file.delete();
|
||||
|
@ -25,7 +25,7 @@ class _LoginRouteState extends State<LoginRoute> {
|
||||
return LoginRouteContainer(
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(tr("Login to Comunic")),
|
||||
title: Text(tr("Login to Comunic")!),
|
||||
),
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
@ -44,7 +44,7 @@ class _LoginRouteState extends State<LoginRoute> {
|
||||
),
|
||||
ListTile(
|
||||
leading: Icon(Icons.help),
|
||||
title: Text(tr("Password forgotten")),
|
||||
title: Text(tr("Password forgotten")!),
|
||||
onTap: _openResetPasswordPage,
|
||||
)
|
||||
],
|
||||
|
@ -50,7 +50,7 @@ abstract class MainController extends State<MainRoute> {
|
||||
int get numberOfPages => _pagesStack.length;
|
||||
|
||||
/// Get current instance of Home controller
|
||||
static MainController of(BuildContext context) {
|
||||
static MainController? of(BuildContext context) {
|
||||
assert(context != null); // A future implementation might need context again
|
||||
return mainControllerKey.currentState;
|
||||
}
|
||||
@ -75,7 +75,7 @@ abstract class MainController extends State<MainRoute> {
|
||||
|
||||
/// Pop until main route is reached
|
||||
void popUntilMainRoute() => Navigator.of(context).popUntil((settings) =>
|
||||
ModalRoute.of(context).isCurrent || !ModalRoute.of(context).isActive);
|
||||
ModalRoute.of(context)!.isCurrent || !ModalRoute.of(context)!.isActive);
|
||||
|
||||
/// Go to the previous page (use for [WillPop] widget)
|
||||
@protected
|
||||
@ -109,13 +109,13 @@ abstract class MainController extends State<MainRoute> {
|
||||
pushPage(PageInfo(child: UserAccessDeniedScreen(userID: userID)));
|
||||
|
||||
/// Open current user page
|
||||
void openCurrentUserPage() => this.openUserPage(userID());
|
||||
void openCurrentUserPage() => this.openUserPage(userID()!);
|
||||
|
||||
/// Open a specific group page specified by its [groupID]
|
||||
///
|
||||
/// [conversationID] is an optional parameter specifying a conversation
|
||||
/// that should be opened instead of posts thread
|
||||
void openGroup(int groupID, {int conversationID}) => pushPage(PageInfo(
|
||||
void openGroup(int groupID, {int? conversationID}) => pushPage(PageInfo(
|
||||
type: PageType.GROUP_PAGE,
|
||||
child: GroupPageScreen(
|
||||
groupID: groupID,
|
||||
@ -137,10 +137,10 @@ abstract class MainController extends State<MainRoute> {
|
||||
void openGroupsListPage() => pushPage(PageInfo(child: GroupsListScreen()));
|
||||
|
||||
/// Display the list of friends of a user
|
||||
void openUserFriendsList(int id) {
|
||||
void openUserFriendsList(int? id) {
|
||||
if (id != userID())
|
||||
pushPage(PageInfo(
|
||||
child: OtherUserFriendsListScreen(userID: id),
|
||||
child: OtherUserFriendsListScreen(userID: id!),
|
||||
canShowAsDialog: true,
|
||||
));
|
||||
else
|
||||
@ -156,9 +156,9 @@ abstract class MainController extends State<MainRoute> {
|
||||
/// Open a conversation in its context (in group page for group conversations)
|
||||
void openConversation(Conversation conv, {fullScreen: false}) {
|
||||
if (conv.isGroupConversation)
|
||||
openGroup(conv.groupID, conversationID: conv.id);
|
||||
openGroup(conv.groupID!, conversationID: conv.id);
|
||||
else
|
||||
openConversationById(conv.id, fullScreen: fullScreen);
|
||||
openConversationById(conv.id!, fullScreen: fullScreen);
|
||||
}
|
||||
|
||||
/// Open a conversation
|
||||
@ -173,7 +173,7 @@ abstract class MainController extends State<MainRoute> {
|
||||
/// Open conversation settings route
|
||||
void openConversationSettingsRoute(Conversation conv) => pushPage(PageInfo(
|
||||
child: UpdateConversationRoute(
|
||||
conversationID: conv.id,
|
||||
conversationID: conv.id!,
|
||||
),
|
||||
canShowAsDialog: true,
|
||||
hideNavBar: true,
|
||||
@ -215,7 +215,7 @@ abstract class MainController extends State<MainRoute> {
|
||||
///
|
||||
/// If the current route is not the main route, we pop one page
|
||||
void popPage() {
|
||||
if (!ModalRoute.of(context).isCurrent)
|
||||
if (!ModalRoute.of(context)!.isCurrent)
|
||||
Navigator.of(context).pop();
|
||||
else
|
||||
doPopPage();
|
||||
|
@ -18,7 +18,7 @@ enum PageType {
|
||||
class PageInfo {
|
||||
final PageType type;
|
||||
final Widget child;
|
||||
final int id;
|
||||
final int? id;
|
||||
final bool hideNavBar;
|
||||
final bool canShowAsDialog;
|
||||
|
||||
@ -27,7 +27,7 @@ class PageInfo {
|
||||
|
||||
PageInfo({
|
||||
this.type = PageType.OTHER_PAGE,
|
||||
@required this.child,
|
||||
required this.child,
|
||||
this.id,
|
||||
this.hideNavBar = false,
|
||||
this.canShowAsDialog = false,
|
||||
|
@ -10,7 +10,7 @@ import 'package:flutter/material.dart';
|
||||
/// @author Pierre HUBERT
|
||||
|
||||
class SmartphoneMainRoute extends StatefulWidget implements MainRoute {
|
||||
const SmartphoneMainRoute({Key key}) : super(key: key);
|
||||
const SmartphoneMainRoute({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _MainRouteState();
|
||||
|
@ -19,7 +19,7 @@ import 'package:flutter/material.dart';
|
||||
const _SideBarSize = 300.0;
|
||||
|
||||
class TabletRoute extends StatefulWidget implements MainRoute {
|
||||
const TabletRoute({Key key}) : super(key: key);
|
||||
const TabletRoute({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
_TabletRouteState createState() => _TabletRouteState();
|
||||
@ -37,7 +37,7 @@ class _TabletRouteState extends MainController {
|
||||
return WillPopScope(
|
||||
onWillPop: willPop,
|
||||
child: Scaffold(
|
||||
appBar: _buildAppBar(),
|
||||
appBar: _buildAppBar() as PreferredSizeWidget?,
|
||||
body: _buildBody(),
|
||||
),
|
||||
);
|
||||
@ -105,14 +105,14 @@ class _TabletRouteState extends MainController {
|
||||
}
|
||||
|
||||
@override
|
||||
void openConversationById(int convID, {fullScreen = false}) {
|
||||
void openConversationById(int? convID, {fullScreen = false}) {
|
||||
if (!fullScreen) {
|
||||
popUntilMainRoute();
|
||||
_conversationsKey.currentState.openConversations(convID);
|
||||
_conversationsKey.currentState!.openConversations(convID);
|
||||
} else
|
||||
super.openConversationById(convID, fullScreen: fullScreen);
|
||||
super.openConversationById(convID!, fullScreen: fullScreen);
|
||||
}
|
||||
|
||||
@override
|
||||
void startCall(int convID) => _callsKey.currentState.openCall(convID);
|
||||
void startCall(int convID) => _callsKey.currentState!.openCall(convID);
|
||||
}
|
||||
|
@ -17,8 +17,8 @@ class PasswordResetRoute extends StatelessWidget {
|
||||
final String token;
|
||||
|
||||
const PasswordResetRoute({
|
||||
Key key,
|
||||
@required this.token,
|
||||
Key? key,
|
||||
required this.token,
|
||||
}) : assert(token != null),
|
||||
super(key: key);
|
||||
|
||||
@ -27,7 +27,7 @@ class PasswordResetRoute extends StatelessWidget {
|
||||
return LoginRouteContainer(
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(tr("Change your password")),
|
||||
title: Text(tr("Change your password")!),
|
||||
),
|
||||
body: _PasswordResetBody(token: token),
|
||||
),
|
||||
@ -38,7 +38,7 @@ class PasswordResetRoute extends StatelessWidget {
|
||||
class _PasswordResetBody extends StatefulWidget {
|
||||
final String token;
|
||||
|
||||
const _PasswordResetBody({Key key, @required this.token})
|
||||
const _PasswordResetBody({Key? key, required this.token})
|
||||
: assert(token != null),
|
||||
super(key: key);
|
||||
|
||||
@ -52,7 +52,7 @@ class __PasswordResetBodyState extends SafeState<_PasswordResetBody> {
|
||||
final _key = GlobalKey<AsyncScreenWidgetState>();
|
||||
|
||||
var _status = _Status.BEFORE_CHANGE;
|
||||
ResCheckPasswordToken _tokenInfo;
|
||||
late ResCheckPasswordToken _tokenInfo;
|
||||
|
||||
void _setStatus(_Status s) => setState(() => _status = s);
|
||||
|
||||
@ -68,7 +68,7 @@ class __PasswordResetBodyState extends SafeState<_PasswordResetBody> {
|
||||
onReload: _validateToken, // The first time, we validate the token
|
||||
onBuild: _buildBody,
|
||||
errorMessage: tr(
|
||||
"Could not validate your password reset token! Maybe it has expired now..."),
|
||||
"Could not validate your password reset token! Maybe it has expired now...")!,
|
||||
);
|
||||
}
|
||||
|
||||
@ -79,10 +79,10 @@ class __PasswordResetBodyState extends SafeState<_PasswordResetBody> {
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Text(tr("You can choose a new password.")),
|
||||
Text(tr("You can choose a new password.")!),
|
||||
OutlinedButton(
|
||||
onPressed: _changePassword,
|
||||
child: Text(tr("Choose a new password")),
|
||||
child: Text(tr("Choose a new password")!),
|
||||
)
|
||||
],
|
||||
));
|
||||
@ -97,12 +97,12 @@ class __PasswordResetBodyState extends SafeState<_PasswordResetBody> {
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
tr("Congratulations! Your password has now been successfully changed!"),
|
||||
tr("Congratulations! Your password has now been successfully changed!")!,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
OutlinedButton(
|
||||
onPressed: _quitScreen,
|
||||
child: Text(tr("Login")),
|
||||
child: Text(tr("Login")!),
|
||||
)
|
||||
],
|
||||
));
|
||||
@ -130,10 +130,10 @@ class __PasswordResetBodyState extends SafeState<_PasswordResetBody> {
|
||||
_setStatus(_Status.AFTER_CHANGE);
|
||||
} catch (e, s) {
|
||||
print("Could not change user password! $e\n$s");
|
||||
showSimpleSnack(context, tr("Could not change your password!"));
|
||||
showSimpleSnack(context, tr("Could not change your password!")!);
|
||||
|
||||
// We start everything again
|
||||
_key.currentState.refresh();
|
||||
_key.currentState!.refresh();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -52,9 +52,9 @@ class _PushNotificationsConfigurationRouteState
|
||||
Spacer(),
|
||||
OutlinedButton(
|
||||
onPressed: _key?.currentState?.canSubmit ?? false
|
||||
? _key.currentState.submit
|
||||
? _key.currentState!.submit
|
||||
: null,
|
||||
child: Text(tr("Configure").toUpperCase()),
|
||||
child: Text(tr("Configure")!.toUpperCase()),
|
||||
style: OutlinedButton.styleFrom(primary: Colors.white)),
|
||||
Spacer(),
|
||||
],
|
||||
@ -69,9 +69,9 @@ class PushNotificationsConfigurationWidget extends StatefulWidget {
|
||||
final Function() onChanged;
|
||||
|
||||
const PushNotificationsConfigurationWidget({
|
||||
Key key,
|
||||
@required this.onConfigured,
|
||||
@required this.onChanged,
|
||||
Key? key,
|
||||
required this.onConfigured,
|
||||
required this.onChanged,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
@ -81,7 +81,7 @@ class PushNotificationsConfigurationWidget extends StatefulWidget {
|
||||
|
||||
class PushNotificationsConfigurationWidgetState
|
||||
extends State<PushNotificationsConfigurationWidget> {
|
||||
PushNotificationsStatus currStatus;
|
||||
PushNotificationsStatus? currStatus;
|
||||
|
||||
bool get canSubmit =>
|
||||
(currStatus ?? PushNotificationsStatus.UNDEFINED) !=
|
||||
@ -92,7 +92,7 @@ class PushNotificationsConfigurationWidgetState
|
||||
currStatus = await PushNotificationsHelper.getLocalStatus();
|
||||
|
||||
if (currStatus == PushNotificationsStatus.UNDEFINED &&
|
||||
srvConfig.notificationsPolicy.hasFirebase)
|
||||
srvConfig!.notificationsPolicy.hasFirebase)
|
||||
currStatus = PushNotificationsStatus.FIREBASE;
|
||||
|
||||
widget.onChanged();
|
||||
@ -103,7 +103,7 @@ class PushNotificationsConfigurationWidgetState
|
||||
return AsyncScreenWidget(
|
||||
onReload: _refresh,
|
||||
onBuild: _buildForm,
|
||||
errorMessage: tr("Failed to load push notifications settings!"),
|
||||
errorMessage: tr("Failed to load push notifications settings!")!,
|
||||
);
|
||||
}
|
||||
|
||||
@ -114,16 +114,16 @@ class PushNotificationsConfigurationWidgetState
|
||||
Text(
|
||||
tr("%app% can send push notifications to your device.", args: {
|
||||
"app": config().appName,
|
||||
}),
|
||||
})!,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
SizedBox(height: 10),
|
||||
_NotificationOption(
|
||||
title: tr("Use Google services (recommended)"),
|
||||
title: tr("Use Google services (recommended)")!,
|
||||
description: tr("Save your battery life."),
|
||||
option: PushNotificationsStatus.FIREBASE,
|
||||
current: currStatus,
|
||||
available: srvConfig.notificationsPolicy.hasFirebase,
|
||||
current: currStatus!,
|
||||
available: srvConfig!.notificationsPolicy.hasFirebase,
|
||||
onChanged: (s) {
|
||||
setState(() => currStatus = s);
|
||||
if (widget.onChanged != null) widget.onChanged();
|
||||
@ -131,13 +131,13 @@ class PushNotificationsConfigurationWidgetState
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
_NotificationOption(
|
||||
title: tr("Use independent notifications service"),
|
||||
title: tr("Use independent notifications service")!,
|
||||
description: tr(
|
||||
"Protect more your privacy, but drains battery and is less reliable."),
|
||||
option: PushNotificationsStatus.INDEPENDENT,
|
||||
current: currStatus,
|
||||
current: currStatus!,
|
||||
available:
|
||||
srvConfig.notificationsPolicy.hasIndependent && isAndroid,
|
||||
srvConfig!.notificationsPolicy.hasIndependent && isAndroid,
|
||||
onChanged: (s) {
|
||||
setState(() => currStatus = s);
|
||||
if (widget.onChanged != null) widget.onChanged();
|
||||
@ -145,9 +145,9 @@ class PushNotificationsConfigurationWidgetState
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
_NotificationOption(
|
||||
title: tr("Do not send notification"),
|
||||
title: tr("Do not send notification")!,
|
||||
option: PushNotificationsStatus.DISABLED,
|
||||
current: currStatus,
|
||||
current: currStatus!,
|
||||
available: true,
|
||||
onChanged: (s) {
|
||||
setState(() => currStatus = s);
|
||||
@ -181,20 +181,20 @@ class PushNotificationsConfigurationWidgetState
|
||||
|
||||
class _NotificationOption extends StatelessWidget {
|
||||
final String title;
|
||||
final String description;
|
||||
final String? description;
|
||||
final PushNotificationsStatus option;
|
||||
final PushNotificationsStatus current;
|
||||
final bool available;
|
||||
final Function(PushNotificationsStatus) onChanged;
|
||||
final Function(PushNotificationsStatus?) onChanged;
|
||||
|
||||
const _NotificationOption({
|
||||
Key key,
|
||||
@required this.title,
|
||||
Key? key,
|
||||
required this.title,
|
||||
this.description,
|
||||
@required this.option,
|
||||
@required this.current,
|
||||
@required this.available,
|
||||
@required this.onChanged,
|
||||
required this.option,
|
||||
required this.current,
|
||||
required this.available,
|
||||
required this.onChanged,
|
||||
}) : assert(title != null),
|
||||
assert(option != null),
|
||||
assert(current != null),
|
||||
@ -219,7 +219,7 @@ class _NotificationOption extends StatelessWidget {
|
||||
title: Text(title, style: TextStyle(color: Colors.white)),
|
||||
subtitle: description == null
|
||||
? null
|
||||
: Text(description, style: TextStyle(color: Colors.white)),
|
||||
: Text(description!, style: TextStyle(color: Colors.white)),
|
||||
contentPadding: EdgeInsets.all(0),
|
||||
onTap: () => onChanged(option),
|
||||
),
|
||||
|
@ -24,16 +24,16 @@ class AboutApplicationSettings extends StatelessWidget {
|
||||
tiles: [
|
||||
SettingsTile(
|
||||
title: tr("Privacy policy"),
|
||||
onPressed: (c) => launch(srvConfig.privacyPolicyURL),
|
||||
onPressed: (c) => launch(srvConfig!.privacyPolicyURL),
|
||||
),
|
||||
SettingsTile(
|
||||
title: tr("Terms of Use"),
|
||||
onPressed: (c) => launch(srvConfig.termsURL),
|
||||
onPressed: (c) => launch(srvConfig!.termsURL),
|
||||
),
|
||||
SettingsTile(
|
||||
title: tr("Contact us"),
|
||||
subtitle: srvConfig.contactEmail,
|
||||
trailing: CopyIcon(srvConfig.contactEmail),
|
||||
subtitle: srvConfig!.contactEmail,
|
||||
trailing: CopyIcon(srvConfig!.contactEmail),
|
||||
),
|
||||
SettingsTile(
|
||||
title: tr("About this application"),
|
||||
|
@ -30,7 +30,7 @@ class AccountImageSettingsScreen extends StatefulWidget {
|
||||
|
||||
class _AccountImageSettingsScreenState
|
||||
extends State<AccountImageSettingsScreen> {
|
||||
AccountImageSettings _settings;
|
||||
late AccountImageSettings _settings;
|
||||
|
||||
final _key = GlobalKey<AsyncScreenWidgetState>();
|
||||
|
||||
@ -49,7 +49,7 @@ class _AccountImageSettingsScreenState
|
||||
onReload: () async =>
|
||||
_settings = await SettingsHelper.getAccountImageSettings(),
|
||||
onBuild: () => _buildLayout(),
|
||||
errorMessage: tr("Could not get account image settings!"),
|
||||
errorMessage: tr("Could not get account image settings!")!,
|
||||
);
|
||||
}
|
||||
|
||||
@ -96,19 +96,19 @@ class _AccountImageSettingsScreenState
|
||||
[
|
||||
MultiChoiceEntry(
|
||||
id: AccountImageVisibilityLevels.EVERYONE,
|
||||
title: tr("Everyone"),
|
||||
title: tr("Everyone")!,
|
||||
subtitle: tr(
|
||||
"Your account image is visible by everyone, including users external to Comunic."),
|
||||
),
|
||||
MultiChoiceEntry(
|
||||
id: AccountImageVisibilityLevels.COMUNIC_USERS,
|
||||
title: tr("Connected users"),
|
||||
title: tr("Connected users")!,
|
||||
subtitle: tr(
|
||||
"Your account image is visible only to connected Comunic users."),
|
||||
),
|
||||
MultiChoiceEntry(
|
||||
id: AccountImageVisibilityLevels.FRIENDS_ONLY,
|
||||
title: tr("My friends"),
|
||||
title: tr("My friends")!,
|
||||
subtitle: tr("Your account image is visible only by your friends."),
|
||||
),
|
||||
];
|
||||
@ -139,14 +139,14 @@ class _AccountImageSettingsScreenState
|
||||
|
||||
// Change account image visibility
|
||||
MultiChoicesSettingsTile(
|
||||
title: tr("Account image visibility"),
|
||||
title: tr("Account image visibility")!,
|
||||
choices: _visibilityLevels,
|
||||
currentValue: _settings.visibility,
|
||||
onChanged: (newLevel) async {
|
||||
onChanged: (dynamic newLevel) async {
|
||||
if (!await SettingsHelper.setAccountImageVisibilityLevel(newLevel))
|
||||
showSimpleSnack(context,
|
||||
tr("Could not update account image visibility level!"));
|
||||
_key.currentState.refresh();
|
||||
tr("Could not update account image visibility level!")!);
|
||||
_key.currentState!.refresh();
|
||||
}),
|
||||
|
||||
// Delete account image
|
||||
@ -160,7 +160,7 @@ class _AccountImageSettingsScreenState
|
||||
/// Upload a new account image
|
||||
void _uploadAccountImage() async {
|
||||
await uploadNewAccountImage(context);
|
||||
_key.currentState.refresh();
|
||||
_key.currentState!.refresh();
|
||||
}
|
||||
|
||||
/// Generate a random account image
|
||||
@ -170,11 +170,11 @@ class _AccountImageSettingsScreenState
|
||||
|
||||
if (!await SettingsHelper.uploadAccountImageFromMemory(bytes)) {
|
||||
showSimpleSnack(
|
||||
context, tr("Could not upload your generated account image!"));
|
||||
context, tr("Could not upload your generated account image!")!);
|
||||
return;
|
||||
}
|
||||
|
||||
_key.currentState.refresh();
|
||||
_key.currentState!.refresh();
|
||||
}
|
||||
|
||||
/// Delete user account image
|
||||
@ -185,11 +185,11 @@ class _AccountImageSettingsScreenState
|
||||
return;
|
||||
|
||||
if (!await SettingsHelper.deleteAccountImage()) {
|
||||
showSimpleSnack(context, tr("Could not delete user account image!"));
|
||||
showSimpleSnack(context, tr("Could not delete user account image!")!);
|
||||
return;
|
||||
}
|
||||
|
||||
_key.currentState.refresh();
|
||||
_key.currentState!.refresh();
|
||||
}
|
||||
}
|
||||
|
||||
@ -203,6 +203,6 @@ Future<void> uploadNewAccountImage(BuildContext context) async {
|
||||
await SettingsHelper.uploadAccountImage(image);
|
||||
} catch (e, s) {
|
||||
logError(e, s);
|
||||
snack(context, tr("Failed to upload new account image!"));
|
||||
snack(context, tr("Failed to upload new account image!")!);
|
||||
}
|
||||
}
|
||||
|
@ -25,9 +25,9 @@ class AccountPrivacySettings extends StatefulWidget {
|
||||
|
||||
class _AccountPrivacySettingsState extends State<AccountPrivacySettings> {
|
||||
final _key = GlobalKey<AsyncScreenWidgetState>();
|
||||
ServerConfig _serverConfig;
|
||||
DataConservationPolicySettings _userSettings;
|
||||
String _cachedPassword;
|
||||
ServerConfig? _serverConfig;
|
||||
late DataConservationPolicySettings _userSettings;
|
||||
String? _cachedPassword;
|
||||
|
||||
Future<void> _loadSettings() async {
|
||||
_serverConfig = ServerConfigurationHelper.config;
|
||||
@ -59,68 +59,68 @@ class _AccountPrivacySettingsState extends State<AccountPrivacySettings> {
|
||||
)
|
||||
])
|
||||
]),
|
||||
errorMessage: tr("Failed to load privacy settings!"),
|
||||
errorMessage: tr("Failed to load privacy settings!")!,
|
||||
);
|
||||
}
|
||||
|
||||
List<SettingsTile> get _dataConservationPolicyTiles => [
|
||||
DataConservationPolicyTile(
|
||||
value: _userSettings.notificationLifetime,
|
||||
title: tr("Automatically delete unread notifications after"),
|
||||
title: tr("Automatically delete unread notifications after")!,
|
||||
onChange: (val) {
|
||||
_userSettings.notificationLifetime = val;
|
||||
_updateDataConservationPolicy();
|
||||
},
|
||||
minValue:
|
||||
_serverConfig.dataConservationPolicy.minNotificationLifetime,
|
||||
_serverConfig!.dataConservationPolicy.minNotificationLifetime,
|
||||
),
|
||||
DataConservationPolicyTile(
|
||||
value: _userSettings.commentsLifetime,
|
||||
title: tr("Automatically delete your comments after"),
|
||||
title: tr("Automatically delete your comments after")!,
|
||||
onChange: (val) {
|
||||
_userSettings.commentsLifetime = val;
|
||||
_updateDataConservationPolicy();
|
||||
},
|
||||
minValue: _serverConfig.dataConservationPolicy.minCommentsLifetime,
|
||||
minValue: _serverConfig!.dataConservationPolicy.minCommentsLifetime,
|
||||
),
|
||||
DataConservationPolicyTile(
|
||||
value: _userSettings.postsLifetime,
|
||||
title: tr("Automatically delete your posts after"),
|
||||
title: tr("Automatically delete your posts after")!,
|
||||
onChange: (val) {
|
||||
_userSettings.postsLifetime = val;
|
||||
_updateDataConservationPolicy();
|
||||
},
|
||||
minValue: _serverConfig.dataConservationPolicy.minPostsLifetime,
|
||||
minValue: _serverConfig!.dataConservationPolicy.minPostsLifetime,
|
||||
),
|
||||
DataConservationPolicyTile(
|
||||
value: _userSettings.conversationMessagesLifetime,
|
||||
title: tr("Automatically delete your conversation messages after"),
|
||||
title: tr("Automatically delete your conversation messages after")!,
|
||||
onChange: (val) {
|
||||
_userSettings.conversationMessagesLifetime = val;
|
||||
_updateDataConservationPolicy();
|
||||
},
|
||||
minValue: _serverConfig
|
||||
minValue: _serverConfig!
|
||||
.dataConservationPolicy.minConversationMessagesLifetime,
|
||||
),
|
||||
DataConservationPolicyTile(
|
||||
value: _userSettings.likesLifetime,
|
||||
title: tr("Automatically delete your likes after"),
|
||||
title: tr("Automatically delete your likes after")!,
|
||||
onChange: (val) {
|
||||
_userSettings.likesLifetime = val;
|
||||
_updateDataConservationPolicy();
|
||||
},
|
||||
minValue: _serverConfig.dataConservationPolicy.minLikesLifetime,
|
||||
minValue: _serverConfig!.dataConservationPolicy.minLikesLifetime,
|
||||
),
|
||||
DataConservationPolicyTile(
|
||||
value: _userSettings.inactiveAccountLifeTime,
|
||||
title: tr(
|
||||
"Automatically delete your account if you have been inactive for"),
|
||||
"Automatically delete your account if you have been inactive for")!,
|
||||
onChange: (val) {
|
||||
_userSettings.inactiveAccountLifeTime = val;
|
||||
_updateDataConservationPolicy();
|
||||
},
|
||||
minValue:
|
||||
_serverConfig.dataConservationPolicy.minInactiveAccountLifetime,
|
||||
_serverConfig!.dataConservationPolicy.minInactiveAccountLifetime,
|
||||
),
|
||||
];
|
||||
|
||||
@ -132,11 +132,11 @@ class _AccountPrivacySettingsState extends State<AccountPrivacySettings> {
|
||||
await SettingsHelper.setDataConservationPolicy(
|
||||
_cachedPassword, _userSettings);
|
||||
|
||||
_key.currentState.refresh();
|
||||
_key.currentState!.refresh();
|
||||
} catch (e, s) {
|
||||
print("Could not update data conservation policy! $e\n$s");
|
||||
showSimpleSnack(
|
||||
context, tr("Failed to update data conservation policy!"));
|
||||
context, tr("Failed to update data conservation policy!")!);
|
||||
}
|
||||
}
|
||||
|
||||
@ -164,22 +164,22 @@ class _AccountPrivacySettingsState extends State<AccountPrivacySettings> {
|
||||
await AccountHelper.deleteAccount(password);
|
||||
} catch (e, stack) {
|
||||
print("Could not delete user account! $e\n$stack");
|
||||
showSimpleSnack(context, tr("Could not delete your account!"));
|
||||
showSimpleSnack(context, tr("Could not delete your account!")!);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class DataConservationPolicyTile extends SettingsTile {
|
||||
final int value;
|
||||
final int? value;
|
||||
final String title;
|
||||
final Function(int) onChange;
|
||||
final int minValue;
|
||||
|
||||
DataConservationPolicyTile({
|
||||
@required this.value,
|
||||
@required this.title,
|
||||
@required this.onChange,
|
||||
@required this.minValue,
|
||||
required this.value,
|
||||
required this.title,
|
||||
required this.onChange,
|
||||
required this.minValue,
|
||||
}) : assert(title != null),
|
||||
assert(onChange != null),
|
||||
assert(minValue != null);
|
||||
@ -203,59 +203,59 @@ class DataConservationPolicyTile extends SettingsTile {
|
||||
int get _roundValue {
|
||||
if (this.value == null) return 0;
|
||||
|
||||
return _choices.firstWhere((element) => element.id >= this.value).id;
|
||||
return _choices.firstWhere((element) => element.id >= this.value!).id;
|
||||
}
|
||||
|
||||
List<MultiChoiceEntry<int>> get _choices => [
|
||||
MultiChoiceEntry(id: 0, title: tr("Never"), hidden: false),
|
||||
MultiChoiceEntry(id: 0, title: tr("Never")!, hidden: false),
|
||||
MultiChoiceEntry(
|
||||
id: _day * 7,
|
||||
title: tr("7 days"),
|
||||
title: tr("7 days")!,
|
||||
hidden: _day * 7 < minValue,
|
||||
),
|
||||
MultiChoiceEntry(
|
||||
id: _day * 15,
|
||||
title: tr("15 days"),
|
||||
title: tr("15 days")!,
|
||||
hidden: _day * 15 < minValue,
|
||||
),
|
||||
MultiChoiceEntry(
|
||||
id: _month,
|
||||
title: tr("1 month"),
|
||||
title: tr("1 month")!,
|
||||
hidden: _month < minValue,
|
||||
),
|
||||
MultiChoiceEntry(
|
||||
id: _month * 3,
|
||||
title: tr("3 months"),
|
||||
title: tr("3 months")!,
|
||||
hidden: _month * 3 < minValue,
|
||||
),
|
||||
MultiChoiceEntry(
|
||||
id: _month * 6,
|
||||
title: tr("6 months"),
|
||||
title: tr("6 months")!,
|
||||
hidden: _month * 6 < minValue,
|
||||
),
|
||||
MultiChoiceEntry(
|
||||
id: _year,
|
||||
title: tr("1 year"),
|
||||
title: tr("1 year")!,
|
||||
hidden: _year < minValue,
|
||||
),
|
||||
MultiChoiceEntry(
|
||||
id: _year * 2,
|
||||
title: tr("2 years"),
|
||||
title: tr("2 years")!,
|
||||
hidden: _year * 5 < minValue,
|
||||
),
|
||||
MultiChoiceEntry(
|
||||
id: _year * 5,
|
||||
title: tr("5 years"),
|
||||
title: tr("5 years")!,
|
||||
hidden: _year * 5 < minValue,
|
||||
),
|
||||
MultiChoiceEntry(
|
||||
id: _year * 10,
|
||||
title: tr("10 years"),
|
||||
title: tr("10 years")!,
|
||||
hidden: _year * 10 < minValue,
|
||||
),
|
||||
MultiChoiceEntry(
|
||||
id: _year * 50,
|
||||
title: tr("50 years"),
|
||||
title: tr("50 years")!,
|
||||
hidden: _year * 50 < minValue,
|
||||
),
|
||||
];
|
||||
@ -265,18 +265,18 @@ class _LastChanceDeleteAccountDialog extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return CupertinoAlertDialog(
|
||||
title: Text(tr("Delete your account")),
|
||||
title: Text(tr("Delete your account")!),
|
||||
content: Text(tr(
|
||||
"Let us ask you one last time. Do you really want to delete your account? If you decide to do so, your data will be permanently removed from our servers, so we will not be able to recover your account. If you decide to proceed, the deletion process will start immediatly and you will automatically get disconnected from your account.")),
|
||||
"Let us ask you one last time. Do you really want to delete your account? If you decide to do so, your data will be permanently removed from our servers, so we will not be able to recover your account. If you decide to proceed, the deletion process will start immediatly and you will automatically get disconnected from your account.")!),
|
||||
actions: <Widget>[
|
||||
CupertinoDialogAction(
|
||||
isDefaultAction: true,
|
||||
child: Text(tr("Cancel")),
|
||||
child: Text(tr("Cancel")!),
|
||||
onPressed: () => Navigator.of(context).pop(false),
|
||||
),
|
||||
CupertinoDialogAction(
|
||||
isDestructiveAction: true,
|
||||
child: Text(tr("Confirm")),
|
||||
child: Text(tr("Confirm")!),
|
||||
onPressed: () => Navigator.of(context).pop(true),
|
||||
),
|
||||
],
|
||||
|
@ -86,10 +86,10 @@ class _AccountSecuritySettingsScreenState
|
||||
await SettingsHelper.changePassword(currPassword, newPassword);
|
||||
|
||||
showSimpleSnack(
|
||||
context, tr("Your password has been successfully changed!"));
|
||||
context, tr("Your password has been successfully changed!")!);
|
||||
} catch (e, stack) {
|
||||
print("Could not update current user password! $e\n$stack");
|
||||
showSimpleSnack(context, tr("Could not update password!"));
|
||||
showSimpleSnack(context, tr("Could not update password!")!);
|
||||
}
|
||||
}
|
||||
|
||||
@ -111,10 +111,10 @@ class _AccountSecuritySettingsScreenState
|
||||
await SettingsHelper.setSecuritySettings(password, newSettings);
|
||||
|
||||
showSimpleSnack(context,
|
||||
tr("You security questions have been successfully updated!"));
|
||||
tr("You security questions have been successfully updated!")!);
|
||||
} catch (e, stack) {
|
||||
print("Could not update security questions!$e\n$stack");
|
||||
showSimpleSnack(context, tr("Could not update security questions!"));
|
||||
showSimpleSnack(context, tr("Could not update security questions!")!);
|
||||
}
|
||||
}
|
||||
|
||||
@ -131,7 +131,7 @@ class _AccountSecuritySettingsScreenState
|
||||
} catch (e, stack) {
|
||||
print("Could not disconnect user on all devices! $e\n$stack");
|
||||
showSimpleSnack(
|
||||
context, tr("Could not disconnect you from all your devices!"));
|
||||
context, tr("Could not disconnect you from all your devices!")!);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -139,7 +139,7 @@ class _AccountSecuritySettingsScreenState
|
||||
class _SecurityQuestionsDialog extends StatefulWidget {
|
||||
final SecuritySettings settings;
|
||||
|
||||
const _SecurityQuestionsDialog({Key key, @required this.settings})
|
||||
const _SecurityQuestionsDialog({Key? key, required this.settings})
|
||||
: assert(settings != null),
|
||||
super(key: key);
|
||||
|
||||
@ -149,10 +149,10 @@ class _SecurityQuestionsDialog extends StatefulWidget {
|
||||
}
|
||||
|
||||
class __SecurityQuestionsDialogState extends State<_SecurityQuestionsDialog> {
|
||||
TextEditingController _controllerQuestion1;
|
||||
TextEditingController _controllerAnswer1;
|
||||
TextEditingController _controllerQuestion2;
|
||||
TextEditingController _controllerAnswer2;
|
||||
late TextEditingController _controllerQuestion1;
|
||||
late TextEditingController _controllerAnswer1;
|
||||
late TextEditingController _controllerQuestion2;
|
||||
late TextEditingController _controllerAnswer2;
|
||||
|
||||
SecuritySettings get _newSettings => SecuritySettings(
|
||||
securityQuestion1: _controllerQuestion1.text,
|
||||
@ -177,16 +177,16 @@ class __SecurityQuestionsDialogState extends State<_SecurityQuestionsDialog> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: Text(tr("Update security questions")),
|
||||
title: Text(tr("Update security questions")!),
|
||||
content: AutoSizeDialogContentWidget(child: _buildContent()),
|
||||
actions: <Widget>[
|
||||
MaterialButton(
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
child: Text(tr("Cancel").toUpperCase()),
|
||||
child: Text(tr("Cancel")!.toUpperCase()),
|
||||
),
|
||||
MaterialButton(
|
||||
onPressed: () => Navigator.of(context).pop(_newSettings),
|
||||
child: Text(tr("Update").toUpperCase()),
|
||||
child: Text(tr("Update")!.toUpperCase()),
|
||||
)
|
||||
],
|
||||
);
|
||||
@ -196,7 +196,7 @@ class __SecurityQuestionsDialogState extends State<_SecurityQuestionsDialog> {
|
||||
return Column(
|
||||
children: <Widget>[
|
||||
Text(tr(
|
||||
"Note: Your two questions and answers MUST be completed in order to be able to recover your account using your security questions!")),
|
||||
"Note: Your two questions and answers MUST be completed in order to be able to recover your account using your security questions!")!),
|
||||
_buildTextField(
|
||||
controller: _controllerQuestion1, label: tr("Question 1")),
|
||||
_buildTextField(controller: _controllerAnswer1, label: tr("Answer 1")),
|
||||
@ -208,8 +208,8 @@ class __SecurityQuestionsDialogState extends State<_SecurityQuestionsDialog> {
|
||||
}
|
||||
|
||||
Widget _buildTextField({
|
||||
@required TextEditingController controller,
|
||||
@required String label,
|
||||
required TextEditingController controller,
|
||||
required String? label,
|
||||
}) {
|
||||
return TextField(
|
||||
controller: controller,
|
||||
|
@ -27,10 +27,10 @@ class _SettingsSection {
|
||||
final Widget Function() onBuild;
|
||||
|
||||
const _SettingsSection({
|
||||
@required this.title,
|
||||
@required this.subtitle,
|
||||
@required this.icon,
|
||||
@required this.onBuild,
|
||||
required this.title,
|
||||
required this.subtitle,
|
||||
required this.icon,
|
||||
required this.onBuild,
|
||||
}) : assert(title != null),
|
||||
assert(subtitle != null),
|
||||
assert(icon != null),
|
||||
@ -42,14 +42,14 @@ class AccountSettingsRoute extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(tr("Settings")),
|
||||
title: Text(tr("Settings")!),
|
||||
actions: [
|
||||
PopupMenuButton<_MainMenuActions>(
|
||||
onSelected: (v) => _doPopupMenuAction(context, v),
|
||||
itemBuilder: (c) => [
|
||||
PopupMenuItem(
|
||||
value: _MainMenuActions.SHOW_TOUR,
|
||||
child: Text(tr("See the tour again")),
|
||||
child: Text(tr("See the tour again")!),
|
||||
),
|
||||
]),
|
||||
],
|
||||
@ -74,7 +74,7 @@ class _AccountSettingsBody extends StatefulWidget {
|
||||
}
|
||||
|
||||
class __AccountSettingsBodyState extends State<_AccountSettingsBody> {
|
||||
_SettingsSection _currentSection;
|
||||
late _SettingsSection _currentSection;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@ -87,64 +87,64 @@ class __AccountSettingsBodyState extends State<_AccountSettingsBody> {
|
||||
/// The list of settings sections
|
||||
List<_SettingsSection> get _sections => [
|
||||
_SettingsSection(
|
||||
title: tr("General settings"),
|
||||
subtitle: tr("Configure the main settings of your account"),
|
||||
title: tr("General settings")!,
|
||||
subtitle: tr("Configure the main settings of your account")!,
|
||||
icon: Icons.settings,
|
||||
onBuild: () => GeneralAccountSettingsScreen(),
|
||||
),
|
||||
|
||||
// Emoticons
|
||||
_SettingsSection(
|
||||
title: tr("Custom emojis"),
|
||||
subtitle: tr("Set your own emoticon shorcuts"),
|
||||
title: tr("Custom emojis")!,
|
||||
subtitle: tr("Set your own emoticon shorcuts")!,
|
||||
icon: Icons.insert_emoticon,
|
||||
onBuild: () => CustomEmojisAccountSettings(),
|
||||
),
|
||||
|
||||
// Account image
|
||||
_SettingsSection(
|
||||
title: tr("Account image"),
|
||||
subtitle: tr("Customize your account image"),
|
||||
title: tr("Account image")!,
|
||||
subtitle: tr("Customize your account image")!,
|
||||
icon: Icons.account_circle,
|
||||
onBuild: () => AccountImageSettingsScreen(),
|
||||
),
|
||||
|
||||
// Notifications settings
|
||||
_SettingsSection(
|
||||
title: tr("Notifications"),
|
||||
subtitle: tr("Choose the notifications you receive on your phone"),
|
||||
title: tr("Notifications")!,
|
||||
subtitle: tr("Choose the notifications you receive on your phone")!,
|
||||
icon: Icons.notifications,
|
||||
onBuild: () => NotificationsSettingsScreen(),
|
||||
),
|
||||
|
||||
// Security settings
|
||||
_SettingsSection(
|
||||
title: tr("Security"),
|
||||
subtitle: tr("Manage security options of your account"),
|
||||
title: tr("Security")!,
|
||||
subtitle: tr("Manage security options of your account")!,
|
||||
icon: Icons.lock,
|
||||
onBuild: () => AccountSecuritySettingsScreen(),
|
||||
),
|
||||
|
||||
// Privacy settings
|
||||
_SettingsSection(
|
||||
title: tr("Privacy"),
|
||||
subtitle: tr("Here you can make actions to protect your privacy"),
|
||||
title: tr("Privacy")!,
|
||||
subtitle: tr("Here you can make actions to protect your privacy")!,
|
||||
icon: Icons.security,
|
||||
onBuild: () => AccountPrivacySettings(),
|
||||
),
|
||||
|
||||
// Application settings
|
||||
_SettingsSection(
|
||||
title: tr("Application settings"),
|
||||
subtitle: tr("Manage local application settings"),
|
||||
title: tr("Application settings")!,
|
||||
subtitle: tr("Manage local application settings")!,
|
||||
icon: Icons.smartphone,
|
||||
onBuild: () => ApplicationSettings(),
|
||||
),
|
||||
|
||||
// About settings
|
||||
_SettingsSection(
|
||||
title: tr("About this application"),
|
||||
subtitle: tr("Learn more about us"),
|
||||
title: tr("About this application")!,
|
||||
subtitle: tr("Learn more about us")!,
|
||||
icon: Icons.info,
|
||||
onBuild: () => AboutApplicationSettings(),
|
||||
),
|
||||
|
@ -17,7 +17,7 @@ class ApplicationSettings extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _ApplicationSettingsState extends State<ApplicationSettings> {
|
||||
PreferencesHelper _preferencesHelper;
|
||||
PreferencesHelper? _preferencesHelper;
|
||||
|
||||
Future<void> _refresh() async {
|
||||
_preferencesHelper = await PreferencesHelper.getInstance();
|
||||
@ -27,7 +27,7 @@ class _ApplicationSettingsState extends State<ApplicationSettings> {
|
||||
Widget build(BuildContext context) => AsyncScreenWidget(
|
||||
onReload: _refresh,
|
||||
onBuild: _buildSections,
|
||||
errorMessage: tr("Could not load settings!"));
|
||||
errorMessage: tr("Could not load settings!")!);
|
||||
|
||||
Widget _buildSections() {
|
||||
return SettingsList(
|
||||
@ -89,17 +89,17 @@ class _ApplicationSettingsState extends State<ApplicationSettings> {
|
||||
|
||||
class _PreferencesSettingsTile extends SettingsTile {
|
||||
final PreferencesKeyList preferencesKey;
|
||||
final String title;
|
||||
final String subtitle;
|
||||
final String? title;
|
||||
final String? subtitle;
|
||||
final Function onChange;
|
||||
final PreferencesHelper helper;
|
||||
final PreferencesHelper? helper;
|
||||
|
||||
_PreferencesSettingsTile({
|
||||
@required this.preferencesKey,
|
||||
@required this.title,
|
||||
@required this.subtitle,
|
||||
@required this.onChange,
|
||||
@required this.helper,
|
||||
required this.preferencesKey,
|
||||
required this.title,
|
||||
required this.subtitle,
|
||||
required this.onChange,
|
||||
required this.helper,
|
||||
});
|
||||
|
||||
@override
|
||||
@ -108,14 +108,14 @@ class _PreferencesSettingsTile extends SettingsTile {
|
||||
title: title,
|
||||
subtitle: subtitle,
|
||||
onToggle: _doChange,
|
||||
switchValue: helper.getBool(preferencesKey),
|
||||
switchValue: helper!.getBool(preferencesKey),
|
||||
titleMaxLines: 2,
|
||||
subtitleMaxLines: 4,
|
||||
);
|
||||
}
|
||||
|
||||
void _doChange(bool value) async {
|
||||
await helper.setBool(preferencesKey, value);
|
||||
await helper!.setBool(preferencesKey, value);
|
||||
onChange();
|
||||
}
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ class CustomEmojisAccountSettings extends StatefulWidget {
|
||||
|
||||
class _CustomEmojisAccountSettingsState
|
||||
extends State<CustomEmojisAccountSettings> {
|
||||
User _user;
|
||||
late User _user;
|
||||
|
||||
final _key = GlobalKey<AsyncScreenWidgetState>();
|
||||
|
||||
@ -45,7 +45,7 @@ class _CustomEmojisAccountSettingsState
|
||||
key: _key,
|
||||
onReload: _reload,
|
||||
onBuild: _buildSettings,
|
||||
errorMessage: tr("Could not refresh user information!"),
|
||||
errorMessage: tr("Could not refresh user information!")!,
|
||||
showOldDataWhileUpdating: true,
|
||||
);
|
||||
}
|
||||
@ -63,8 +63,8 @@ class _CustomEmojisAccountSettingsState
|
||||
return ListView(
|
||||
children: _user.customEmojies
|
||||
.map((u) => ListTile(
|
||||
leading: NetworkImageWidget(url: u.url, width: 50),
|
||||
title: Text(u.shortcut),
|
||||
leading: NetworkImageWidget(url: u.url!, width: 50),
|
||||
title: Text(u.shortcut!),
|
||||
trailing: IconButton(
|
||||
icon: Icon(Icons.delete),
|
||||
onPressed: () => _deleteEmoji(u)),
|
||||
@ -98,10 +98,10 @@ class _CustomEmojisAccountSettingsState
|
||||
await SettingsHelper.uploadNewCustomEmoji(newEmoji);
|
||||
} catch (e, stack) {
|
||||
print("Could not add a new emoji: $e\n$stack");
|
||||
showSimpleSnack(context, tr("Could not upload emoji!"));
|
||||
showSimpleSnack(context, tr("Could not upload emoji!")!);
|
||||
}
|
||||
|
||||
_key.currentState.refresh();
|
||||
_key.currentState!.refresh();
|
||||
}
|
||||
|
||||
/// Ask for confirmation before deleting permanently an emoji
|
||||
@ -115,10 +115,10 @@ class _CustomEmojisAccountSettingsState
|
||||
await SettingsHelper.deleteCustomEmoji(u.id);
|
||||
} catch (e, stack) {
|
||||
print("Could not delete custom emoji! $e\n$stack");
|
||||
showSimpleSnack(context, tr("Could not delete custom emoji!"));
|
||||
showSimpleSnack(context, tr("Could not delete custom emoji!")!);
|
||||
}
|
||||
|
||||
_key.currentState.refresh();
|
||||
_key.currentState!.refresh();
|
||||
}
|
||||
}
|
||||
|
||||
@ -127,8 +127,8 @@ class _NewCustomEmojiDialog extends StatefulWidget {
|
||||
final CustomEmojiesList currentList;
|
||||
|
||||
const _NewCustomEmojiDialog({
|
||||
Key key,
|
||||
@required this.currentList,
|
||||
Key? key,
|
||||
required this.currentList,
|
||||
}) : assert(currentList != null),
|
||||
super(key: key);
|
||||
|
||||
@ -138,7 +138,7 @@ class _NewCustomEmojiDialog extends StatefulWidget {
|
||||
|
||||
class _NewCustomEmojiDialogState extends State<_NewCustomEmojiDialog> {
|
||||
final _controller = TextEditingController();
|
||||
BytesFile _file;
|
||||
BytesFile? _file;
|
||||
|
||||
bool get _hasImage => _file != null;
|
||||
|
||||
@ -151,12 +151,12 @@ class _NewCustomEmojiDialogState extends State<_NewCustomEmojiDialog> {
|
||||
|
||||
bool get _valid => _hasImage && _shortcutValid;
|
||||
|
||||
NewEmoji get _emoji => NewEmoji(shortcut: _shortcut, image: _file);
|
||||
NewEmoji get _emoji => NewEmoji(shortcut: _shortcut, image: _file!);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: Text(tr("Add new emoji")),
|
||||
title: Text(tr("Add new emoji")!),
|
||||
content: ConstrainedBox(
|
||||
constraints:
|
||||
BoxConstraints(maxHeight: MediaQuery.of(context).size.height - 50),
|
||||
@ -167,11 +167,11 @@ class _NewCustomEmojiDialogState extends State<_NewCustomEmojiDialog> {
|
||||
actions: <Widget>[
|
||||
MaterialButton(
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
child: Text(tr("Cancel").toUpperCase()),
|
||||
child: Text(tr("Cancel")!.toUpperCase()),
|
||||
),
|
||||
MaterialButton(
|
||||
onPressed: _valid ? () => Navigator.of(context).pop(_emoji) : null,
|
||||
child: Text(tr("Add").toUpperCase()),
|
||||
child: Text(tr("Add")!.toUpperCase()),
|
||||
),
|
||||
],
|
||||
);
|
||||
@ -194,7 +194,7 @@ class _NewCustomEmojiDialogState extends State<_NewCustomEmojiDialog> {
|
||||
),
|
||||
MaterialButton(
|
||||
onPressed: _pickImage,
|
||||
child: Text(_hasImage ? tr("Replace image") : tr("Add image")),
|
||||
child: Text(_hasImage ? tr("Replace image")! : tr("Add image")!),
|
||||
)
|
||||
],
|
||||
);
|
||||
@ -211,7 +211,7 @@ class _NewCustomEmojiDialogState extends State<_NewCustomEmojiDialog> {
|
||||
});
|
||||
} catch (e, stack) {
|
||||
print("Could not pick an image! $e\n$stack");
|
||||
snack(context, tr("Failed to pick an image!"));
|
||||
snack(context, tr("Failed to pick an image!")!);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ class GeneralAccountSettingsScreen extends StatefulWidget {
|
||||
|
||||
class _GeneralAccountSettingsScreenState
|
||||
extends State<GeneralAccountSettingsScreen> {
|
||||
GeneralSettings _settings;
|
||||
late GeneralSettings _settings;
|
||||
|
||||
final _key = GlobalKey<AsyncScreenWidgetState>();
|
||||
|
||||
@ -47,7 +47,7 @@ class _GeneralAccountSettingsScreenState
|
||||
onReload: () async =>
|
||||
_settings = await SettingsHelper.getGeneralSettings(),
|
||||
onBuild: _buildSettings,
|
||||
errorMessage: tr("Could not load general settings!"),
|
||||
errorMessage: tr("Could not load general settings!")!,
|
||||
showOldDataWhileUpdating: true,
|
||||
);
|
||||
}
|
||||
@ -81,28 +81,28 @@ class _GeneralAccountSettingsScreenState
|
||||
|
||||
// First name
|
||||
TextEditSettingsTile(
|
||||
title: tr("First name"),
|
||||
title: tr("First name")!,
|
||||
currValue: _settings.firstName,
|
||||
onChanged: (s) {
|
||||
_settings.firstName = s;
|
||||
_updateSettings();
|
||||
},
|
||||
maxLength: srvConfig.accountInformationPolicy.maxFirstNameLength,
|
||||
maxLength: srvConfig!.accountInformationPolicy.maxFirstNameLength,
|
||||
checkInput: (s) =>
|
||||
s.length >= srvConfig.accountInformationPolicy.minFirstNameLength,
|
||||
s.length >= srvConfig!.accountInformationPolicy.minFirstNameLength,
|
||||
),
|
||||
|
||||
// Last name
|
||||
TextEditSettingsTile(
|
||||
title: tr("Last name"),
|
||||
title: tr("Last name")!,
|
||||
currValue: _settings.lastName,
|
||||
onChanged: (s) {
|
||||
_settings.lastName = s;
|
||||
_updateSettings();
|
||||
},
|
||||
maxLength: srvConfig.accountInformationPolicy.maxLastNameLength,
|
||||
maxLength: srvConfig!.accountInformationPolicy.maxLastNameLength,
|
||||
checkInput: (s) =>
|
||||
s.length >= srvConfig.accountInformationPolicy.minLastNameLength,
|
||||
s.length >= srvConfig!.accountInformationPolicy.minLastNameLength,
|
||||
),
|
||||
|
||||
// Emails settings
|
||||
@ -121,15 +121,15 @@ class _GeneralAccountSettingsScreenState
|
||||
List<MultiChoiceEntry> get _visibilityChoices => [
|
||||
MultiChoiceEntry(
|
||||
id: UserPageVisibility.PRIVATE,
|
||||
title: tr("Private"),
|
||||
title: tr("Private")!,
|
||||
subtitle: tr("Private, accessible only to your friends")),
|
||||
MultiChoiceEntry(
|
||||
id: UserPageVisibility.PUBLIC,
|
||||
title: tr("Public"),
|
||||
title: tr("Public")!,
|
||||
subtitle: tr("Public, accessible to all Comunic members")),
|
||||
MultiChoiceEntry(
|
||||
id: UserPageVisibility.OPEN,
|
||||
title: tr("Open"),
|
||||
title: tr("Open")!,
|
||||
subtitle:
|
||||
tr("Accessible to everyone, including non-Comunic users")),
|
||||
];
|
||||
@ -139,10 +139,10 @@ class _GeneralAccountSettingsScreenState
|
||||
return [
|
||||
// Page visibility
|
||||
MultiChoicesSettingsTile(
|
||||
title: tr("Page visibility"),
|
||||
title: tr("Page visibility")!,
|
||||
choices: _visibilityChoices,
|
||||
currentValue: _settings.pageVisibility,
|
||||
onChanged: (v) {
|
||||
onChanged: (dynamic v) {
|
||||
_settings.pageVisibility = v;
|
||||
_updateSettings();
|
||||
}),
|
||||
@ -191,7 +191,7 @@ class _GeneralAccountSettingsScreenState
|
||||
|
||||
// Personal website
|
||||
TextEditSettingsTile(
|
||||
title: tr("Personal website URL (optional)"),
|
||||
title: tr("Personal website URL (optional)")!,
|
||||
currValue: _settings.personalWebsite,
|
||||
onChanged: (v) {
|
||||
_settings.personalWebsite = v;
|
||||
@ -203,19 +203,19 @@ class _GeneralAccountSettingsScreenState
|
||||
|
||||
// Location
|
||||
TextEditSettingsTile(
|
||||
title: tr("Location (optional)"),
|
||||
title: tr("Location (optional)")!,
|
||||
currValue: _settings.location ?? "",
|
||||
onChanged: (v) {
|
||||
_settings.location = v;
|
||||
_updateSettings();
|
||||
},
|
||||
maxLength: srvConfig.accountInformationPolicy.maxLocationLength,
|
||||
maxLength: srvConfig!.accountInformationPolicy.maxLocationLength,
|
||||
allowEmptyValues: true,
|
||||
),
|
||||
|
||||
// Public notes
|
||||
TextEditSettingsTile(
|
||||
title: tr("Public note (optional)"),
|
||||
title: tr("Public note (optional)")!,
|
||||
currValue: _settings.publicNote,
|
||||
onChanged: (v) {
|
||||
_settings.publicNote = v;
|
||||
@ -235,7 +235,7 @@ class _GeneralAccountSettingsScreenState
|
||||
context: context,
|
||||
initialDirectory: _settings.virtualDirectory,
|
||||
type: VirtualDirectoryTargetType.USER,
|
||||
id: userID());
|
||||
id: userID()!);
|
||||
|
||||
if (dir == null) return;
|
||||
|
||||
@ -252,8 +252,8 @@ class _GeneralAccountSettingsScreenState
|
||||
await SettingsHelper.updateGeneralSettings(_settings);
|
||||
} catch (e, stack) {
|
||||
print("Error while updating settings! $e/n$stack");
|
||||
showSimpleSnack(context, tr("Could not update general settings!"));
|
||||
showSimpleSnack(context, tr("Could not update general settings!")!);
|
||||
}
|
||||
_key.currentState.refresh();
|
||||
_key.currentState!.refresh();
|
||||
}
|
||||
}
|
||||
|
@ -24,8 +24,8 @@ class _NotificationsSettingsScreenState
|
||||
extends State<NotificationsSettingsScreen> {
|
||||
final key = GlobalKey<AsyncScreenWidgetState>();
|
||||
|
||||
NotificationsSettings _settings;
|
||||
PushNotificationsStatus _pushNotificationsStatus;
|
||||
late NotificationsSettings _settings;
|
||||
PushNotificationsStatus? _pushNotificationsStatus;
|
||||
|
||||
Future<void> _loadSettings() async {
|
||||
_settings = await SettingsHelper.getNotificationsSettings();
|
||||
@ -37,7 +37,7 @@ class _NotificationsSettingsScreenState
|
||||
key: key,
|
||||
onReload: _loadSettings,
|
||||
onBuild: _buildScreen,
|
||||
errorMessage: tr("Failed to load notifications settings!"),
|
||||
errorMessage: tr("Failed to load notifications settings!")!,
|
||||
);
|
||||
|
||||
Widget _buildScreen() => SettingsList(sections: [
|
||||
@ -81,7 +81,7 @@ class _NotificationsSettingsScreenState
|
||||
setState(() {});
|
||||
} catch (e, s) {
|
||||
logError(e, s);
|
||||
snack(context, tr("Failed to update settings!"));
|
||||
snack(context, tr("Failed to update settings!")!);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,8 +12,8 @@ class SinglePostRoute extends StatelessWidget {
|
||||
final int postID;
|
||||
|
||||
const SinglePostRoute({
|
||||
Key key,
|
||||
@required this.postID,
|
||||
Key? key,
|
||||
required this.postID,
|
||||
}) : assert(postID != null),
|
||||
super(key: key);
|
||||
|
||||
@ -21,7 +21,7 @@ class SinglePostRoute extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(tr("Post")),
|
||||
title: Text(tr("Post")!),
|
||||
),
|
||||
body: _SinglePostRouteBody(postID: postID),
|
||||
);
|
||||
@ -32,8 +32,8 @@ class _SinglePostRouteBody extends StatefulWidget {
|
||||
final int postID;
|
||||
|
||||
const _SinglePostRouteBody({
|
||||
Key key,
|
||||
@required this.postID,
|
||||
Key? key,
|
||||
required this.postID,
|
||||
}) : assert(postID != null),
|
||||
super(key: key);
|
||||
|
||||
@ -42,7 +42,7 @@ class _SinglePostRouteBody extends StatefulWidget {
|
||||
}
|
||||
|
||||
class __SinglePostRouteBodyState extends State<_SinglePostRouteBody> {
|
||||
Future<PostsList> _getPost() async {
|
||||
Future<PostsList?> _getPost() async {
|
||||
try {
|
||||
return PostsList()..add(await PostsHelper().getSingle(widget.postID));
|
||||
} on Exception catch (e) {
|
||||
|
@ -40,7 +40,7 @@ class TourRouteState extends State<TourRoute> {
|
||||
|
||||
final Map<int, GlobalKey> pubKeys = Map();
|
||||
|
||||
User currUser;
|
||||
late User currUser;
|
||||
|
||||
int _defaultIndex = 0;
|
||||
|
||||
@ -57,7 +57,7 @@ class TourRouteState extends State<TourRoute> {
|
||||
areNotificationsConfigured = true;
|
||||
|
||||
// Attempt to automatically register to FCM
|
||||
else if (srvConfig.notificationsPolicy.hasFirebase) {
|
||||
else if (srvConfig!.notificationsPolicy.hasFirebase) {
|
||||
try {
|
||||
await PushNotificationsHelper.configure(
|
||||
context, PushNotificationsStatus.FIREBASE);
|
||||
@ -71,12 +71,12 @@ class TourRouteState extends State<TourRoute> {
|
||||
void rebuild() => setState(() {});
|
||||
|
||||
void setStateKeepCurrentIndex(BuildContext cxt) async {
|
||||
_defaultIndex = DefaultTabController.of(cxt).index;
|
||||
await _key.currentState.refresh();
|
||||
_defaultIndex = DefaultTabController.of(cxt)!.index;
|
||||
await _key.currentState!.refresh();
|
||||
}
|
||||
|
||||
List<Widget> get _list => (config().toursEntriesBuilder != null
|
||||
? config().toursEntriesBuilder(this)
|
||||
? config().toursEntriesBuilder!(this)
|
||||
: [
|
||||
FirstTourPane(),
|
||||
|
||||
@ -96,25 +96,25 @@ class TourRouteState extends State<TourRoute> {
|
||||
|
||||
PresentationPane(
|
||||
icon: Icons.group_add,
|
||||
title: tr("Friends"),
|
||||
title: tr("Friends")!,
|
||||
text:
|
||||
"${tr("You can search the people you know and ask them to become your friends!")}\n\n${tr("This will help you to reach them to exchange information!")}",
|
||||
),
|
||||
PresentationPane(
|
||||
icon: Icons.question_answer,
|
||||
title: tr("Conversations"),
|
||||
title: tr("Conversations")!,
|
||||
text:
|
||||
"${tr("With Comunic, you can have conversations with all your friends.")}\n\n${tr("It is also possible to make video calls!")}",
|
||||
),
|
||||
PresentationPane(
|
||||
icon: Icons.group,
|
||||
title: tr("Groups"),
|
||||
title: tr("Groups")!,
|
||||
text:
|
||||
"${tr("You can join groups where people share the same interests as you!")}\n\n${tr("It is also easy to create your own groups!")}",
|
||||
),
|
||||
PresentationPane(
|
||||
icon: Icons.lock,
|
||||
title: tr("Privacy"),
|
||||
title: tr("Privacy")!,
|
||||
text:
|
||||
"${tr("Your data is YOUR DATA. We will never use it or sell it.")}\n\n${tr("If you do not trust us, you can always check out our source code to verify it!")}",
|
||||
),
|
||||
@ -135,7 +135,7 @@ class TourRouteState extends State<TourRoute> {
|
||||
child: AsyncScreenWidget(
|
||||
key: _key,
|
||||
onReload: _init,
|
||||
errorMessage: tr("Failed to load tour!"),
|
||||
errorMessage: tr("Failed to load tour!")!,
|
||||
onBuild: () => DefaultTabController(
|
||||
initialIndex: _defaultIndex,
|
||||
length: _list.length,
|
||||
@ -150,22 +150,22 @@ class TourRouteState extends State<TourRoute> {
|
||||
}
|
||||
|
||||
class _RouteBody extends StatefulWidget {
|
||||
final List<Widget> panes;
|
||||
final List<Widget>? panes;
|
||||
|
||||
const _RouteBody({Key key, this.panes}) : super(key: key);
|
||||
const _RouteBody({Key? key, this.panes}) : super(key: key);
|
||||
|
||||
@override
|
||||
__RouteBodyState createState() => __RouteBodyState();
|
||||
}
|
||||
|
||||
class __RouteBodyState extends State<_RouteBody> {
|
||||
TabController _controller;
|
||||
TabController? _controller;
|
||||
|
||||
bool get _isLastPane => _controller.index >= widget.panes.length - 1;
|
||||
bool get _isLastPane => _controller!.index >= widget.panes!.length - 1;
|
||||
|
||||
PresentationPane get _currPane =>
|
||||
widget.panes[_controller.index] is PresentationPane
|
||||
? widget.panes[_controller.index]
|
||||
PresentationPane? get _currPane =>
|
||||
widget.panes![_controller!.index] is PresentationPane
|
||||
? widget.panes![_controller!.index] as PresentationPane?
|
||||
: null;
|
||||
|
||||
bool get _canGoNext => _currPane?.canGoNext ?? true;
|
||||
@ -184,7 +184,7 @@ class __RouteBodyState extends State<_RouteBody> {
|
||||
|
||||
void _updateController() {
|
||||
_controller = DefaultTabController.of(context);
|
||||
_controller.addListener(() => setState(() {}));
|
||||
_controller!.addListener(() => setState(() {}));
|
||||
}
|
||||
|
||||
@override
|
||||
@ -195,7 +195,7 @@ class __RouteBodyState extends State<_RouteBody> {
|
||||
Spacer(flex: 1),
|
||||
Expanded(
|
||||
child: TabBarView(
|
||||
children: widget.panes,
|
||||
children: widget.panes!,
|
||||
physics: NeverScrollableScrollPhysics(),
|
||||
),
|
||||
flex: 10),
|
||||
@ -204,7 +204,7 @@ class __RouteBodyState extends State<_RouteBody> {
|
||||
Spacer(flex: 1),
|
||||
OutlinedButton(
|
||||
onPressed: _canGoNext ? _nextOrFinish : null,
|
||||
child: Text(!_isLastPane ? tr("Next") : tr("Let's go!")),
|
||||
child: Text(!_isLastPane ? tr("Next")! : tr("Let's go!")!),
|
||||
),
|
||||
Spacer(flex: 1),
|
||||
],
|
||||
@ -212,17 +212,17 @@ class __RouteBodyState extends State<_RouteBody> {
|
||||
);
|
||||
|
||||
void _nextOrFinish() async {
|
||||
if (_controller.indexIsChanging) return;
|
||||
if (_controller!.indexIsChanging) return;
|
||||
|
||||
if (!_isLastPane) {
|
||||
// Check if the next click has to be captured
|
||||
if (_currPane?.onTapNext != null) {
|
||||
if (!await _currPane.onTapNext(context)) return;
|
||||
if (!await _currPane!.onTapNext!(context)) return;
|
||||
}
|
||||
|
||||
_controller.animateTo(_controller.index + 1);
|
||||
_controller!.animateTo(_controller!.index + 1);
|
||||
} else {
|
||||
(await PreferencesHelper.getInstance())
|
||||
(await PreferencesHelper.getInstance())!
|
||||
.setBool(PreferencesKeyList.IS_TOUR_SEEN, true);
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ import 'package:flutter/material.dart';
|
||||
class UpdateConversationRoute extends StatefulWidget {
|
||||
final int conversationID;
|
||||
|
||||
const UpdateConversationRoute({Key key, this.conversationID})
|
||||
const UpdateConversationRoute({Key? key, required this.conversationID})
|
||||
: assert(conversationID != null),
|
||||
super(key: key);
|
||||
|
||||
|
@ -13,8 +13,8 @@ class VideoPlayerRoute extends StatefulWidget {
|
||||
final String url;
|
||||
|
||||
const VideoPlayerRoute({
|
||||
Key key,
|
||||
@required this.url,
|
||||
Key? key,
|
||||
required this.url,
|
||||
}) : assert(url != null),
|
||||
super(key: key);
|
||||
|
||||
@ -23,16 +23,16 @@ class VideoPlayerRoute extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _VideoPlayerRouteState extends State<VideoPlayerRoute> {
|
||||
VideoPlayerController _videoPlayerController;
|
||||
ChewieController _chewieController;
|
||||
VideoPlayerController? _videoPlayerController;
|
||||
ChewieController? _chewieController;
|
||||
|
||||
Future<void> _initialize() async {
|
||||
_videoPlayerController = VideoPlayerController.network(widget.url);
|
||||
|
||||
await _videoPlayerController.initialize();
|
||||
await _videoPlayerController!.initialize();
|
||||
|
||||
_chewieController = ChewieController(
|
||||
videoPlayerController: _videoPlayerController,
|
||||
videoPlayerController: _videoPlayerController!,
|
||||
looping: false,
|
||||
allowFullScreen: true,
|
||||
allowMuting: true,
|
||||
@ -42,8 +42,8 @@ class _VideoPlayerRouteState extends State<VideoPlayerRoute> {
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
if (_videoPlayerController != null) _videoPlayerController.dispose();
|
||||
if (_chewieController != null) _chewieController.dispose();
|
||||
if (_videoPlayerController != null) _videoPlayerController!.dispose();
|
||||
if (_chewieController != null) _chewieController!.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@ -61,8 +61,8 @@ class _VideoPlayerRouteState extends State<VideoPlayerRoute> {
|
||||
Widget _buildBody() => AsyncScreenWidget(
|
||||
onReload: _initialize,
|
||||
onBuild: _showBody,
|
||||
errorMessage: tr("Failed to initialize video!"),
|
||||
errorMessage: tr("Failed to initialize video!")!,
|
||||
);
|
||||
|
||||
Widget _showBody() => Chewie(controller: _chewieController);
|
||||
Widget _showBody() => Chewie(controller: _chewieController!);
|
||||
}
|
||||
|
@ -32,12 +32,12 @@ class _WelcomeRouteState extends State<WelcomeRoute> {
|
||||
children: <Widget>[
|
||||
ListTile(
|
||||
leading: Icon(Icons.login, color: Colors.white),
|
||||
title: Text(tr("Login")),
|
||||
title: Text(tr("Login")!),
|
||||
onTap: _openLoginPage,
|
||||
),
|
||||
ListTile(
|
||||
leading: Icon(Icons.person_add_alt_1, color: Colors.white),
|
||||
title: Text(tr("Join the network")),
|
||||
title: Text(tr("Join the network")!),
|
||||
onTap: _openCreateAccountPage,
|
||||
)
|
||||
],
|
||||
|
@ -27,14 +27,14 @@ Color get _headerColor => Colors.blueAccent.shade700;
|
||||
|
||||
class AuthorizedGroupPageScreen extends StatefulWidget {
|
||||
final AdvancedGroupInfo advancedGroupInfo;
|
||||
final int conversationID;
|
||||
final int? conversationID;
|
||||
final Function() needRefresh;
|
||||
|
||||
const AuthorizedGroupPageScreen({
|
||||
Key key,
|
||||
@required this.advancedGroupInfo,
|
||||
@required this.conversationID,
|
||||
@required this.needRefresh,
|
||||
Key? key,
|
||||
required this.advancedGroupInfo,
|
||||
required this.conversationID,
|
||||
required this.needRefresh,
|
||||
}) : assert(advancedGroupInfo != null),
|
||||
assert(needRefresh != null),
|
||||
super(key: key);
|
||||
@ -49,42 +49,42 @@ class _AuthorizedGroupPageScreenState
|
||||
with SingleTickerProviderStateMixin {
|
||||
AdvancedGroupInfo get _group => widget.advancedGroupInfo;
|
||||
|
||||
TabController _tabController;
|
||||
TabController? _tabController;
|
||||
|
||||
List<_GroupPageTab> get _tabs => [
|
||||
// Posts list
|
||||
_GroupPageTab(
|
||||
widget: (c) => GroupPostsSection(group: _group),
|
||||
label: tr("Posts"),
|
||||
label: tr("Posts")!,
|
||||
),
|
||||
|
||||
// Forez presence tab
|
||||
_GroupPageTab(
|
||||
widget: (c) => ForezPresenceSection(groupID: _group.id),
|
||||
label: tr("Presence"),
|
||||
label: tr("Presence")!,
|
||||
visible: _group.isForezGroup,
|
||||
),
|
||||
|
||||
// About the group
|
||||
_GroupPageTab(
|
||||
widget: (c) => AboutGroupSection(group: _group),
|
||||
label: tr("About"),
|
||||
label: tr("About")!,
|
||||
),
|
||||
|
||||
_GroupPageTab(
|
||||
widget: (c) => GroupMembersSection(groupID: _group.id),
|
||||
label: tr("Members"),
|
||||
visible: _group.isAtLeastModerator || _group.isMembersListPublic,
|
||||
label: tr("Members")!,
|
||||
visible: _group.isAtLeastModerator || _group.isMembersListPublic!,
|
||||
)
|
||||
].where((element) => element.visible).toList()
|
||||
|
||||
// Add group conversations
|
||||
..insertAll(
|
||||
(_group.isForezGroup ? 2 : 1),
|
||||
_group.conversations
|
||||
_group.conversations!
|
||||
.map((e) => _GroupPageTab(
|
||||
widget: (c) => GroupConversationSection(conv: e),
|
||||
label: e.name))
|
||||
label: e.name!))
|
||||
.toList());
|
||||
|
||||
@override
|
||||
@ -94,7 +94,7 @@ class _AuthorizedGroupPageScreenState
|
||||
initialIndex: widget.conversationID == null
|
||||
? 0
|
||||
: (_group.isForezGroup ? 2 : 1) +
|
||||
_group.conversations
|
||||
_group.conversations!
|
||||
.indexWhere((element) => element.id == widget.conversationID),
|
||||
vsync: this,
|
||||
);
|
||||
@ -104,7 +104,7 @@ class _AuthorizedGroupPageScreenState
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_tabController.dispose();
|
||||
_tabController!.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@ -189,7 +189,7 @@ class _AuthorizedGroupPageScreenState
|
||||
_group.isAdmin
|
||||
? IconButton(
|
||||
icon: Icon(Icons.settings, color: _headerTextColor),
|
||||
onPressed: () => MainController.of(context).push(
|
||||
onPressed: () => MainController.of(context)!.push(
|
||||
GroupSettingsScreen(groupID: _group.id),
|
||||
canShowAsDialog: true))
|
||||
: Container(),
|
||||
@ -207,9 +207,9 @@ class _GroupPageTab {
|
||||
final String label;
|
||||
|
||||
const _GroupPageTab({
|
||||
@required this.widget,
|
||||
required this.widget,
|
||||
this.visible = true,
|
||||
@required this.label,
|
||||
required this.label,
|
||||
}) : assert(widget != null),
|
||||
assert(visible != null),
|
||||
assert(label != null);
|
||||
|
@ -33,16 +33,16 @@ class CallScreen extends StatefulWidget {
|
||||
|
||||
/// Use custom application bar. This function takes as parameter a nullable
|
||||
/// String which is the title of the conversation
|
||||
final PreferredSizeWidget Function(String) buildCustomAppBar;
|
||||
final PreferredSizeWidget Function(String?)? buildCustomAppBar;
|
||||
|
||||
/// Execute custom action when the call is closed
|
||||
///
|
||||
/// The default behavior is to pop the page
|
||||
final void Function() onClose;
|
||||
final void Function()? onClose;
|
||||
|
||||
const CallScreen({
|
||||
Key key,
|
||||
@required this.convID,
|
||||
Key? key,
|
||||
required this.convID,
|
||||
this.floatingButtons = true,
|
||||
this.buildCustomAppBar,
|
||||
this.onClose,
|
||||
@ -60,15 +60,15 @@ class _CallScreenState extends SafeState<CallScreen> {
|
||||
int get convID => widget.convID;
|
||||
|
||||
// State properties
|
||||
Conversation _conversation;
|
||||
String _convName;
|
||||
CallConfig _conf;
|
||||
late Conversation _conversation;
|
||||
String? _convName;
|
||||
late CallConfig _conf;
|
||||
var _error = false;
|
||||
CallMembersList _membersList;
|
||||
UsersList _usersList;
|
||||
final _peersConnections = Map<int, RTCPeerConnection>();
|
||||
final _renderers = Map<int, RTCVideoRenderer>();
|
||||
MediaStream _localStream;
|
||||
CallMembersList? _membersList;
|
||||
late UsersList _usersList;
|
||||
final _peersConnections = Map<int?, RTCPeerConnection>();
|
||||
final _renderers = Map<int?, RTCVideoRenderer>();
|
||||
MediaStream? _localStream;
|
||||
var _isLocalStreamVisible = true;
|
||||
var _hideMenuBars = false;
|
||||
|
||||
@ -81,16 +81,16 @@ class _CallScreenState extends SafeState<CallScreen> {
|
||||
_conversation.callCapabilities == CallCapabilities.VIDEO;
|
||||
|
||||
bool get isStreamingAudio =>
|
||||
_localStream != null && _localStream.getAudioTracks().length > 0;
|
||||
_localStream != null && _localStream!.getAudioTracks().length > 0;
|
||||
|
||||
bool get isStreamingVideo =>
|
||||
_localStream != null && _localStream.getVideoTracks().length > 0;
|
||||
_localStream != null && _localStream!.getVideoTracks().length > 0;
|
||||
|
||||
bool get isAudioMuted =>
|
||||
isStreamingAudio && !_localStream.getAudioTracks()[0].enabled;
|
||||
isStreamingAudio && !_localStream!.getAudioTracks()[0].enabled;
|
||||
|
||||
bool get isVideoMuted =>
|
||||
isStreamingVideo && !_localStream.getVideoTracks()[0].enabled;
|
||||
isStreamingVideo && !_localStream!.getVideoTracks()[0].enabled;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@ -137,7 +137,7 @@ class _CallScreenState extends SafeState<CallScreen> {
|
||||
try {
|
||||
if (!_usersList.hasUser(e.userID))
|
||||
_usersList.add(await UsersHelper().getSingleWithThrow(e.userID));
|
||||
setState(() => _membersList.add(CallMember(userID: e.userID)));
|
||||
setState(() => _membersList!.add(CallMember(userID: e.userID!)));
|
||||
} catch (e, stack) {
|
||||
print("$e\n$stack");
|
||||
}
|
||||
@ -164,7 +164,7 @@ class _CallScreenState extends SafeState<CallScreen> {
|
||||
});
|
||||
|
||||
// Connect to ready peers
|
||||
for (final peer in _membersList.readyPeers)
|
||||
for (final peer in _membersList!.readyPeers)
|
||||
await this._memberReady(peer.userID);
|
||||
|
||||
setState(() {});
|
||||
@ -183,7 +183,7 @@ class _CallScreenState extends SafeState<CallScreen> {
|
||||
await setWakeLock(false);
|
||||
|
||||
// Close all ready members
|
||||
for (final member in _membersList.readyPeers)
|
||||
for (final member in _membersList!.readyPeers)
|
||||
await _removeMember(member.userID);
|
||||
|
||||
// Close local stream
|
||||
@ -204,9 +204,9 @@ class _CallScreenState extends SafeState<CallScreen> {
|
||||
message: tr("Do you really want to leave this call ?"))) return;
|
||||
|
||||
if (widget.onClose == null)
|
||||
MainController.of(context).popPage();
|
||||
MainController.of(context)!.popPage();
|
||||
else
|
||||
widget.onClose();
|
||||
widget.onClose!();
|
||||
}
|
||||
|
||||
/// Start streaming on our end
|
||||
@ -232,8 +232,8 @@ class _CallScreenState extends SafeState<CallScreen> {
|
||||
|
||||
// Start renderer
|
||||
_renderers[userID()] = RTCVideoRenderer();
|
||||
await _renderers[userID()].initialize();
|
||||
_renderers[userID()].srcObject = _localStream;
|
||||
await _renderers[userID()]!.initialize();
|
||||
_renderers[userID()]!.srcObject = _localStream;
|
||||
setState(() {});
|
||||
|
||||
// Open stream
|
||||
@ -244,8 +244,8 @@ class _CallScreenState extends SafeState<CallScreen> {
|
||||
|
||||
_peersConnections[userID()] = peerConnection;
|
||||
|
||||
for (final track in _localStream.getTracks()) {
|
||||
await peerConnection.addTrack(track, _localStream);
|
||||
for (final track in _localStream!.getTracks()) {
|
||||
await peerConnection.addTrack(track, _localStream!);
|
||||
}
|
||||
|
||||
peerConnection.onAddStream =
|
||||
@ -275,7 +275,7 @@ class _CallScreenState extends SafeState<CallScreen> {
|
||||
await CallsHelper.sendSessionDescription(convID, userID(), offer);
|
||||
} catch (e, stack) {
|
||||
print("Could not start streaming! $e\n$stack");
|
||||
showSimpleSnack(context, tr("Could not start streaming!"));
|
||||
showSimpleSnack(context, tr("Could not start streaming!")!);
|
||||
}
|
||||
}
|
||||
|
||||
@ -283,7 +283,7 @@ class _CallScreenState extends SafeState<CallScreen> {
|
||||
Future<void> _stopStreaming() async {
|
||||
// Close peer connection
|
||||
if (_peersConnections.containsKey(userID())) {
|
||||
_peersConnections[userID()].close();
|
||||
_peersConnections[userID()]!.close();
|
||||
_peersConnections.remove(userID());
|
||||
|
||||
await CallsHelper.notifyStoppedStreaming(convID);
|
||||
@ -291,13 +291,13 @@ class _CallScreenState extends SafeState<CallScreen> {
|
||||
|
||||
// Stop local stream
|
||||
if (_localStream != null) {
|
||||
await _localStream.dispose();
|
||||
await _localStream!.dispose();
|
||||
_localStream = null;
|
||||
}
|
||||
|
||||
// Close renderer
|
||||
if (_renderers.containsKey(userID())) {
|
||||
await _renderers[userID()].dispose();
|
||||
await _renderers[userID()]!.dispose();
|
||||
_renderers.remove(userID());
|
||||
}
|
||||
}
|
||||
@ -324,20 +324,20 @@ class _CallScreenState extends SafeState<CallScreen> {
|
||||
// Toggle appropriate mute
|
||||
else {
|
||||
if (!isVideo)
|
||||
_localStream.getAudioTracks()[0].enabled =
|
||||
!_localStream.getAudioTracks()[0].enabled;
|
||||
_localStream!.getAudioTracks()[0].enabled =
|
||||
!_localStream!.getAudioTracks()[0].enabled;
|
||||
else
|
||||
_localStream.getVideoTracks()[0].enabled =
|
||||
!_localStream.getVideoTracks()[0].enabled;
|
||||
_localStream!.getVideoTracks()[0].enabled =
|
||||
!_localStream!.getVideoTracks()[0].enabled;
|
||||
}
|
||||
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
/// Call this when a user started to stream media
|
||||
Future<void> _memberReady(int memberID) async {
|
||||
Future<void> _memberReady(int? memberID) async {
|
||||
try {
|
||||
_membersList.getUser(memberID).status = MemberStatus.READY;
|
||||
_membersList!.getUser(memberID).status = MemberStatus.READY;
|
||||
setState(() {});
|
||||
|
||||
// Create peer connection
|
||||
@ -353,15 +353,15 @@ class _CallScreenState extends SafeState<CallScreen> {
|
||||
|
||||
// Create a renderer
|
||||
_renderers[memberID] = RTCVideoRenderer();
|
||||
await _renderers[memberID].initialize();
|
||||
await _renderers[memberID]!.initialize();
|
||||
|
||||
// Register callbacks
|
||||
peerConnection.onIceCandidate =
|
||||
(c) => CallsHelper.sendIceCandidate(convID, memberID, c);
|
||||
peerConnection.onAddStream = (s) {
|
||||
setState(() {
|
||||
_membersList.getUser(memberID).stream = s;
|
||||
_renderers[memberID].srcObject = s;
|
||||
_membersList!.getUser(memberID).stream = s;
|
||||
_renderers[memberID]!.srcObject = s;
|
||||
});
|
||||
};
|
||||
|
||||
@ -371,7 +371,7 @@ class _CallScreenState extends SafeState<CallScreen> {
|
||||
setState(() {});
|
||||
} catch (e, stack) {
|
||||
print("Could not connect to remote peer $e\n$stack!");
|
||||
showSimpleSnack(context, tr("Could not connect to a remote peer!"));
|
||||
showSimpleSnack(context, tr("Could not connect to a remote peer!")!);
|
||||
}
|
||||
}
|
||||
|
||||
@ -388,13 +388,13 @@ class _CallScreenState extends SafeState<CallScreen> {
|
||||
// Check the kind of signal
|
||||
// SessionDescription
|
||||
if (ev.sessionDescription != null) {
|
||||
await _peersConnections[ev.peerID]
|
||||
.setRemoteDescription(ev.sessionDescription);
|
||||
await _peersConnections[ev.peerID]!
|
||||
.setRemoteDescription(ev.sessionDescription!);
|
||||
|
||||
// Send answer if required
|
||||
if (ev.sessionDescription.type == "offer") {
|
||||
final answer = await _peersConnections[ev.peerID].createAnswer({});
|
||||
await _peersConnections[ev.peerID].setLocalDescription(answer);
|
||||
if (ev.sessionDescription!.type == "offer") {
|
||||
final answer = await _peersConnections[ev.peerID]!.createAnswer({});
|
||||
await _peersConnections[ev.peerID]!.setLocalDescription(answer);
|
||||
|
||||
await CallsHelper.sendSessionDescription(convID, ev.peerID, answer);
|
||||
}
|
||||
@ -402,41 +402,41 @@ class _CallScreenState extends SafeState<CallScreen> {
|
||||
|
||||
// Ice Candidate
|
||||
else {
|
||||
await _peersConnections[ev.peerID].addCandidate(ev.candidate);
|
||||
await _peersConnections[ev.peerID]!.addCandidate(ev.candidate!);
|
||||
}
|
||||
} catch (e, stack) {
|
||||
print("Error while handling new signal ! $e\n$stack");
|
||||
showSimpleSnack(context, tr("Error while processing new signal!"));
|
||||
showSimpleSnack(context, tr("Error while processing new signal!")!);
|
||||
}
|
||||
}
|
||||
|
||||
/// Call this when a user has interrupted streaming
|
||||
Future<void> _removeRemotePeerConnection(int memberID) async {
|
||||
final member = _membersList.getUser(memberID);
|
||||
Future<void> _removeRemotePeerConnection(int? memberID) async {
|
||||
final member = _membersList!.getUser(memberID);
|
||||
member.status = MemberStatus.JOINED;
|
||||
setState(() {});
|
||||
|
||||
if (_peersConnections.containsKey(memberID)) {
|
||||
await _peersConnections[memberID].close();
|
||||
await _peersConnections[memberID]!.close();
|
||||
_peersConnections.remove(memberID);
|
||||
}
|
||||
|
||||
if (_renderers.containsKey(memberID)) {
|
||||
await _renderers[memberID].dispose();
|
||||
await _renderers[memberID]!.dispose();
|
||||
_renderers.remove(memberID);
|
||||
}
|
||||
|
||||
if (member.stream != null) {
|
||||
member.stream.dispose();
|
||||
member.stream!.dispose();
|
||||
member.stream = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// Call this when a member has completely left the call
|
||||
Future<void> _removeMember(int memberID) async {
|
||||
Future<void> _removeMember(int? memberID) async {
|
||||
await _removeRemotePeerConnection(memberID);
|
||||
|
||||
_membersList.removeUser(memberID);
|
||||
_membersList!.removeUser(memberID);
|
||||
|
||||
setState(() {});
|
||||
}
|
||||
@ -453,7 +453,7 @@ class _CallScreenState extends SafeState<CallScreen> {
|
||||
switch (option) {
|
||||
// Switch camera
|
||||
case _PopupMenuOption.SWITCH_CAMERA:
|
||||
await Helper.switchCamera(_localStream.getVideoTracks()[0]);
|
||||
await Helper.switchCamera(_localStream!.getVideoTracks()[0]);
|
||||
break;
|
||||
|
||||
// Stop streaming
|
||||
@ -478,14 +478,14 @@ class _CallScreenState extends SafeState<CallScreen> {
|
||||
/// Build application bar
|
||||
PreferredSizeWidget _buildAppBar() {
|
||||
if (widget.buildCustomAppBar != null)
|
||||
return widget.buildCustomAppBar(_convName);
|
||||
return widget.buildCustomAppBar!(_convName);
|
||||
|
||||
return AppBar(
|
||||
leading: IconButton(
|
||||
icon: Icon(Icons.arrow_back),
|
||||
onPressed: () => _leaveCall(),
|
||||
),
|
||||
title: _convName == null ? CircularProgressIndicator() : Text(_convName),
|
||||
title: _convName == null ? CircularProgressIndicator() : Text(_convName!),
|
||||
);
|
||||
}
|
||||
|
||||
@ -496,7 +496,7 @@ class _CallScreenState extends SafeState<CallScreen> {
|
||||
return buildErrorCard(tr("Could not initialize call!"), actions: [
|
||||
MaterialButton(
|
||||
onPressed: () => _initCall(),
|
||||
child: Text(tr("Try again").toUpperCase()),
|
||||
child: Text(tr("Try again")!.toUpperCase()),
|
||||
)
|
||||
]);
|
||||
|
||||
@ -526,7 +526,7 @@ class _CallScreenState extends SafeState<CallScreen> {
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: RichText(
|
||||
text: TextSpan(
|
||||
children: _membersList
|
||||
children: _membersList!
|
||||
.map((f) => TextSpan(
|
||||
text: _usersList.getUser(f.userID).displayName + " ",
|
||||
style: TextStyle(
|
||||
@ -540,7 +540,7 @@ class _CallScreenState extends SafeState<CallScreen> {
|
||||
|
||||
/// Videos area
|
||||
Widget _buildVideosArea(BuildContext context, BoxConstraints constraints) {
|
||||
final availableVideos = _membersList.readyPeers
|
||||
final List<CallMember> availableVideos = _membersList!.readyPeers
|
||||
.where((f) => f.hasVideoStream && _renderers.containsKey(f.userID))
|
||||
.toList();
|
||||
|
||||
@ -591,12 +591,12 @@ class _CallScreenState extends SafeState<CallScreen> {
|
||||
}
|
||||
|
||||
Widget _buildMemberVideo(int peerID) {
|
||||
return RTCVideoView(_renderers[peerID]);
|
||||
return RTCVideoView(_renderers[peerID]!);
|
||||
}
|
||||
|
||||
Widget _buildLocalVideo() {
|
||||
return Positioned(
|
||||
child: RTCVideoView(_renderers[userID()]),
|
||||
child: RTCVideoView(_renderers[userID()]!),
|
||||
height: 50,
|
||||
width: 50,
|
||||
right: 10,
|
||||
@ -657,12 +657,12 @@ class _CallScreenState extends SafeState<CallScreen> {
|
||||
// Switch camera
|
||||
PopupMenuItem(
|
||||
enabled: isStreamingVideo,
|
||||
child: Text(tr("Switch camera")),
|
||||
child: Text(tr("Switch camera")!),
|
||||
value: _PopupMenuOption.SWITCH_CAMERA),
|
||||
|
||||
// Interrupt streaming
|
||||
PopupMenuItem(
|
||||
child: Text(tr("Stop streaming")),
|
||||
child: Text(tr("Stop streaming")!),
|
||||
value: _PopupMenuOption.STOP_STREAMING)
|
||||
],
|
||||
child: _FooterButton(
|
||||
@ -679,7 +679,7 @@ class _CallScreenState extends SafeState<CallScreen> {
|
||||
}
|
||||
|
||||
class _FooterButton extends StatelessWidget {
|
||||
final Function() onPressed;
|
||||
final Function()? onPressed;
|
||||
final Widget icon;
|
||||
final bool visible;
|
||||
final double width;
|
||||
@ -687,13 +687,13 @@ class _FooterButton extends StatelessWidget {
|
||||
final bool roundedButtons;
|
||||
|
||||
const _FooterButton({
|
||||
Key key,
|
||||
@required this.icon,
|
||||
@required this.onPressed,
|
||||
Key? key,
|
||||
required this.icon,
|
||||
required this.onPressed,
|
||||
this.visible = true,
|
||||
this.width = 45,
|
||||
this.bgColor = Colors.black,
|
||||
@required this.roundedButtons,
|
||||
required this.roundedButtons,
|
||||
}) : assert(icon != null),
|
||||
assert(visible != null),
|
||||
assert(roundedButtons != null),
|
||||
@ -712,7 +712,7 @@ class _FooterButton extends StatelessWidget {
|
||||
child: FloatingActionButton(
|
||||
child: icon,
|
||||
foregroundColor: Colors.white,
|
||||
onPressed: onPressed == null ? null : () => onPressed(),
|
||||
onPressed: onPressed == null ? null : () => onPressed!(),
|
||||
backgroundColor: bgColor,
|
||||
),
|
||||
));
|
||||
|
@ -15,8 +15,8 @@ class ConversationMembersScreen extends StatefulWidget {
|
||||
final int convID;
|
||||
|
||||
const ConversationMembersScreen({
|
||||
Key key,
|
||||
@required this.convID,
|
||||
Key? key,
|
||||
required this.convID,
|
||||
}) : assert(convID != null),
|
||||
super(key: key);
|
||||
|
||||
@ -26,8 +26,8 @@ class ConversationMembersScreen extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _ConversationMembersScreenState extends State<ConversationMembersScreen> {
|
||||
Conversation _conversation;
|
||||
UsersList _members;
|
||||
late Conversation _conversation;
|
||||
late UsersList _members;
|
||||
|
||||
Future<void> _refresh() async {
|
||||
_conversation =
|
||||
@ -38,28 +38,28 @@ class _ConversationMembersScreenState extends State<ConversationMembersScreen> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: Text(tr("Conversation members"))),
|
||||
appBar: AppBar(title: Text(tr("Conversation members")!)),
|
||||
body: AsyncScreenWidget(
|
||||
onReload: _refresh,
|
||||
onBuild: _buildMembersList,
|
||||
errorMessage:
|
||||
tr("Could not load the list of members of this conversation!"),
|
||||
tr("Could not load the list of members of this conversation!")!,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildMembersList() => ListView.builder(
|
||||
itemBuilder: _buildItem,
|
||||
itemCount: _conversation.members.length,
|
||||
itemCount: _conversation.members!.length,
|
||||
);
|
||||
|
||||
Widget _buildItem(BuildContext context, int index) {
|
||||
final member = _conversation.members[index];
|
||||
final member = _conversation.members![index];
|
||||
final user = _members.getUser(member.userID);
|
||||
return ListTile(
|
||||
leading: AccountImageWidget(user: user),
|
||||
title: Text(user.displayName),
|
||||
subtitle: Text(member.isAdmin ? tr("Admin") : tr("Member")),
|
||||
subtitle: Text(member.isAdmin ? tr("Admin")! : tr("Member")!),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -42,7 +42,7 @@ enum _OlderMessagesLevel { NONE, LOADING, NO_MORE_AVAILABLE }
|
||||
class ConversationScreen extends StatefulWidget {
|
||||
final int conversationID;
|
||||
|
||||
const ConversationScreen({Key key, this.conversationID})
|
||||
const ConversationScreen({Key? key, required this.conversationID})
|
||||
: assert(conversationID != null),
|
||||
super(key: key);
|
||||
|
||||
@ -56,8 +56,8 @@ class _ConversationScreenState extends SafeState<ConversationScreen> {
|
||||
final UsersHelper _usersHelper = UsersHelper();
|
||||
|
||||
// Class members
|
||||
Conversation _conversation;
|
||||
ConversationMessagesList _messages;
|
||||
late Conversation _conversation;
|
||||
ConversationMessagesList? _messages;
|
||||
UsersList _usersInfo = UsersList();
|
||||
ErrorLevel _error = ErrorLevel.NONE;
|
||||
final _textFieldFocus = FocusNode();
|
||||
@ -66,21 +66,21 @@ class _ConversationScreenState extends SafeState<ConversationScreen> {
|
||||
|
||||
bool _isSendingMessage = false;
|
||||
TextEditingController _textController = TextEditingController();
|
||||
ScrollWatcher _scrollController;
|
||||
ScrollWatcher? _scrollController;
|
||||
_OlderMessagesLevel _loadingOlderMessages = _OlderMessagesLevel.NONE;
|
||||
|
||||
int _lastWritingEventSent = 0;
|
||||
|
||||
CancelToken _sendCancel;
|
||||
double _sendProgress;
|
||||
CancelToken? _sendCancel;
|
||||
double? _sendProgress;
|
||||
|
||||
String get textMessage => _textController.text;
|
||||
|
||||
bool get _isMessageValid =>
|
||||
textMessage.length >=
|
||||
ServerConfigurationHelper.config.conversationsPolicy.minMessageLen &&
|
||||
ServerConfigurationHelper.config!.conversationsPolicy.minMessageLen &&
|
||||
textMessage.length <
|
||||
ServerConfigurationHelper.config.conversationsPolicy.maxMessageLen;
|
||||
ServerConfigurationHelper.config!.conversationsPolicy.maxMessageLen;
|
||||
|
||||
showKeyboard() => _textFieldFocus.requestFocus();
|
||||
|
||||
@ -179,14 +179,14 @@ class _ConversationScreenState extends SafeState<ConversationScreen> {
|
||||
this.listen<UpdatedConversationMessageEvent>((ev) async {
|
||||
if (ev.msg.convID == widget.conversationID) {
|
||||
await _conversationsHelper.saveMessage(ev.msg);
|
||||
setState(() => _messages.replace(ev.msg));
|
||||
setState(() => _messages!.replace(ev.msg));
|
||||
}
|
||||
});
|
||||
|
||||
this.listen<DeletedConversationMessageEvent>((ev) async {
|
||||
if (ev.msg.convID == widget.conversationID) {
|
||||
await _conversationsHelper.removeMessage(ev.msg);
|
||||
setState(() => _messages.removeMsg(ev.msg.id));
|
||||
setState(() => _messages!.removeMsg(ev.msg.id));
|
||||
}
|
||||
});
|
||||
|
||||
@ -216,7 +216,7 @@ class _ConversationScreenState extends SafeState<ConversationScreen> {
|
||||
//First, get the messages
|
||||
final messages = await _conversationsHelper.getNewMessages(
|
||||
conversationID: widget.conversationID,
|
||||
lastMessageID: _messages == null ? 0 : _messages.lastMessageID,
|
||||
lastMessageID: _messages == null ? 0 : _messages!.lastMessageID,
|
||||
online: online,
|
||||
);
|
||||
|
||||
@ -235,14 +235,14 @@ class _ConversationScreenState extends SafeState<ConversationScreen> {
|
||||
Future<void> _loadOlderMessages() async {
|
||||
if (_loadingOlderMessages != _OlderMessagesLevel.NONE ||
|
||||
_messages == null ||
|
||||
_messages.length == 0) return;
|
||||
_messages!.length == 0) return;
|
||||
try {
|
||||
// Let's start to load older messages
|
||||
_setLoadingOlderMessagesState(_OlderMessagesLevel.LOADING);
|
||||
|
||||
final messages = await _conversationsHelper.getOlderMessages(
|
||||
conversationID: widget.conversationID,
|
||||
oldestMessagesID: _messages.firstMessageID);
|
||||
oldestMessagesID: _messages!.firstMessageID);
|
||||
|
||||
// Mark as not loading anymore
|
||||
_setLoadingOlderMessagesState(_OlderMessagesLevel.NONE);
|
||||
@ -280,14 +280,14 @@ class _ConversationScreenState extends SafeState<ConversationScreen> {
|
||||
if (_messages == null)
|
||||
_messages = messages;
|
||||
else
|
||||
_messages.addAll(messages);
|
||||
_messages!.addAll(messages);
|
||||
|
||||
//Reverse the order of the messages (if required)
|
||||
if (messages.length > 0) {
|
||||
_messages.sort();
|
||||
final reverse = _messages.reversed;
|
||||
_messages!.sort();
|
||||
final Iterable<ConversationMessage> reverse = _messages!.reversed;
|
||||
_messages = ConversationMessagesList();
|
||||
_messages.addAll(reverse);
|
||||
_messages!.addAll(reverse);
|
||||
}
|
||||
});
|
||||
|
||||
@ -300,20 +300,20 @@ class _ConversationScreenState extends SafeState<ConversationScreen> {
|
||||
try {
|
||||
final file = await showPickFileDialog(
|
||||
context: context,
|
||||
maxFileSize: srvConfig.conversationsPolicy.filesMaxSize,
|
||||
allowedMimeTypes: srvConfig.conversationsPolicy.allowedFilesType,
|
||||
imageMaxWidth: srvConfig.conversationsPolicy.maxMessageImageWidth,
|
||||
imageMaxHeight: srvConfig.conversationsPolicy.maxMessageImageHeight,
|
||||
maxFileSize: srvConfig!.conversationsPolicy.filesMaxSize,
|
||||
allowedMimeTypes: srvConfig!.conversationsPolicy.allowedFilesType,
|
||||
imageMaxWidth: srvConfig!.conversationsPolicy.maxMessageImageWidth,
|
||||
imageMaxHeight: srvConfig!.conversationsPolicy.maxMessageImageHeight,
|
||||
);
|
||||
|
||||
if (file == null) return;
|
||||
|
||||
BytesFile thumbnail;
|
||||
BytesFile? thumbnail;
|
||||
|
||||
if (isVideo(lookupMimeType(file.filename)))
|
||||
if (isVideo(lookupMimeType(file.filename)!))
|
||||
thumbnail = await generateVideoThumbnail(
|
||||
videoFile: file,
|
||||
maxWidth: srvConfig.conversationsPolicy.maxThumbnailWidth,
|
||||
maxWidth: srvConfig!.conversationsPolicy.maxThumbnailWidth,
|
||||
);
|
||||
|
||||
_sendCancel = CancelToken();
|
||||
@ -331,7 +331,7 @@ class _ConversationScreenState extends SafeState<ConversationScreen> {
|
||||
assert(res == SendMessageResult.SUCCESS);
|
||||
} catch (e, s) {
|
||||
logError(e, s);
|
||||
showSimpleSnack(context, tr("Failed to send a file!"));
|
||||
showSimpleSnack(context, tr("Failed to send a file!")!);
|
||||
}
|
||||
|
||||
setState(() {
|
||||
@ -363,8 +363,8 @@ class _ConversationScreenState extends SafeState<ConversationScreen> {
|
||||
SnackBar(
|
||||
content: Text(
|
||||
result == SendMessageResult.MESSAGE_REJECTED
|
||||
? tr("Message rejected by the server!")
|
||||
: tr("Could not send message!"),
|
||||
? tr("Message rejected by the server!")!
|
||||
: tr("Could not send message!")!,
|
||||
),
|
||||
duration: Duration(milliseconds: 500),
|
||||
),
|
||||
@ -395,7 +395,7 @@ class _ConversationScreenState extends SafeState<ConversationScreen> {
|
||||
Widget _buildNoMessagesNotice() {
|
||||
return Expanded(
|
||||
child: Center(
|
||||
child: Text(tr("There is no message yet in this conversation.")),
|
||||
child: Text(tr("There is no message yet in this conversation.")!),
|
||||
),
|
||||
);
|
||||
}
|
||||
@ -406,15 +406,15 @@ class _ConversationScreenState extends SafeState<ConversationScreen> {
|
||||
child: ListView.builder(
|
||||
controller: _scrollController,
|
||||
reverse: true,
|
||||
itemCount: _messages.length,
|
||||
itemCount: _messages!.length,
|
||||
itemBuilder: (c, i) => _buildMessageItem(i),
|
||||
));
|
||||
}
|
||||
|
||||
Widget _buildMessageItem(int msgIndex) {
|
||||
final msg = _messages[msgIndex];
|
||||
final msg = _messages![msgIndex];
|
||||
final nextMessage =
|
||||
msgIndex + 1 < _messages.length ? _messages[msgIndex + 1] : null;
|
||||
msgIndex + 1 < _messages!.length ? _messages![msgIndex + 1] : null;
|
||||
|
||||
return Column(
|
||||
children: <Widget>[
|
||||
@ -427,7 +427,7 @@ class _ConversationScreenState extends SafeState<ConversationScreen> {
|
||||
? Container(
|
||||
alignment: Alignment.center,
|
||||
child: ServerConversationMessageTile(
|
||||
message: msg.serverMessage, users: _usersInfo),
|
||||
message: msg.serverMessage!, users: _usersInfo),
|
||||
)
|
||||
: Container(
|
||||
margin: EdgeInsets.symmetric(vertical: 5),
|
||||
@ -442,7 +442,7 @@ class _ConversationScreenState extends SafeState<ConversationScreen> {
|
||||
}
|
||||
|
||||
Widget _buildSenderLayout(
|
||||
ConversationMessage message, ConversationMessage previousMessage) {
|
||||
ConversationMessage message, ConversationMessage? previousMessage) {
|
||||
final messageRadius = Radius.circular(10);
|
||||
|
||||
return Container(
|
||||
@ -466,7 +466,7 @@ class _ConversationScreenState extends SafeState<ConversationScreen> {
|
||||
}
|
||||
|
||||
Widget _buildReceiverLayout(
|
||||
ConversationMessage message, ConversationMessage previousMessage) {
|
||||
ConversationMessage message, ConversationMessage? previousMessage) {
|
||||
final messageRadius = Radius.circular(10);
|
||||
|
||||
return Row(children: [
|
||||
@ -640,11 +640,11 @@ class _ConversationScreenState extends SafeState<ConversationScreen> {
|
||||
flex: 5,
|
||||
),
|
||||
Spacer(flex: 1),
|
||||
Text("${(_sendProgress * 100).toInt()}%"),
|
||||
Text("${(_sendProgress! * 100).toInt()}%"),
|
||||
Spacer(flex: 1),
|
||||
OutlinedButton(
|
||||
onPressed: () => _sendCancel.cancel(),
|
||||
child: Text(tr("Cancel").toUpperCase()),
|
||||
onPressed: () => _sendCancel!.cancel(),
|
||||
child: Text(tr("Cancel")!.toUpperCase()),
|
||||
),
|
||||
Spacer(flex: 1),
|
||||
],
|
||||
@ -667,8 +667,8 @@ class _ConversationScreenState extends SafeState<ConversationScreen> {
|
||||
? _buildLoadingOlderMessage()
|
||||
: null,
|
||||
),
|
||||
_messages.length == 0 ? _buildNoMessagesNotice() : _buildMessagesList(),
|
||||
UserWritingInConvNotifier(convID: _conversation.id),
|
||||
_messages!.length == 0 ? _buildNoMessagesNotice() : _buildMessagesList(),
|
||||
UserWritingInConvNotifier(convID: _conversation.id!),
|
||||
_sendCancel != null ? _buildSendingWidget() : _buildSendMessageForm(),
|
||||
_showEmojiPicker ? _buildEmojiContainer() : Container(),
|
||||
],
|
||||
@ -681,7 +681,7 @@ class _ConversationScreenState extends SafeState<ConversationScreen> {
|
||||
final t = time();
|
||||
|
||||
if (t - _lastWritingEventSent <
|
||||
srvConfig.conversationsPolicy.writingEventInterval) return;
|
||||
srvConfig!.conversationsPolicy.writingEventInterval) return;
|
||||
|
||||
_lastWritingEventSent = t;
|
||||
await ConversationsHelper.sendWritingEvent(_conversation.id);
|
||||
@ -692,7 +692,7 @@ class _ConversationScreenState extends SafeState<ConversationScreen> {
|
||||
|
||||
/// Request message statistics
|
||||
void _requestMessageStats(ConversationMessage message) async {
|
||||
MainController.of(context)
|
||||
MainController.of(context)!
|
||||
.openConversationMessageStats(_conversation, message);
|
||||
}
|
||||
|
||||
@ -700,20 +700,20 @@ class _ConversationScreenState extends SafeState<ConversationScreen> {
|
||||
Future<void> _updateMessage(ConversationMessage message) async {
|
||||
final newContent = await askUserString(
|
||||
context: context,
|
||||
title: tr("Update message"),
|
||||
message: tr("Please enter new message content:"),
|
||||
defaultValue: message.message.content,
|
||||
hint: tr("New message"),
|
||||
title: tr("Update message")!,
|
||||
message: tr("Please enter new message content:")!,
|
||||
defaultValue: message.message.content!,
|
||||
hint: tr("New message")!,
|
||||
minLength:
|
||||
ServerConfigurationHelper.config.conversationsPolicy.minMessageLen,
|
||||
ServerConfigurationHelper.config!.conversationsPolicy.minMessageLen,
|
||||
maxLength:
|
||||
ServerConfigurationHelper.config.conversationsPolicy.maxMessageLen,
|
||||
ServerConfigurationHelper.config!.conversationsPolicy.maxMessageLen,
|
||||
);
|
||||
|
||||
if (newContent == null) return;
|
||||
|
||||
if (!await _conversationsHelper.updateMessage(message.id, newContent)) {
|
||||
showSimpleSnack(context, tr("Could not update message content!"));
|
||||
showSimpleSnack(context, tr("Could not update message content!")!);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -723,21 +723,21 @@ class _ConversationScreenState extends SafeState<ConversationScreen> {
|
||||
final choice = await showDialog<bool>(
|
||||
context: context,
|
||||
builder: (c) => AlertDialog(
|
||||
title: Text(tr("Confirm deletion")),
|
||||
title: Text(tr("Confirm deletion")!),
|
||||
content: Text(
|
||||
tr("Do you really want to delete this message ? The operation can not be cancelled !"),
|
||||
tr("Do you really want to delete this message ? The operation can not be cancelled !")!,
|
||||
textAlign: TextAlign.justify,
|
||||
),
|
||||
actions: <Widget>[
|
||||
TextButton(
|
||||
child: Text(
|
||||
tr("Cancel").toUpperCase(),
|
||||
tr("Cancel")!.toUpperCase(),
|
||||
),
|
||||
onPressed: () => Navigator.pop(c, false),
|
||||
),
|
||||
TextButton(
|
||||
child: Text(
|
||||
tr("Confirm").toUpperCase(),
|
||||
tr("Confirm")!.toUpperCase(),
|
||||
style: TextStyle(color: Colors.red),
|
||||
),
|
||||
onPressed: () => Navigator.pop(c, true),
|
||||
@ -750,6 +750,6 @@ class _ConversationScreenState extends SafeState<ConversationScreen> {
|
||||
|
||||
// Execute the request
|
||||
if (!await _conversationsHelper.deleteMessage(message.id))
|
||||
showSimpleSnack(context, tr("Could not delete conversation message!"));
|
||||
showSimpleSnack(context, tr("Could not delete conversation message!")!);
|
||||
}
|
||||
}
|
||||
|
@ -21,10 +21,10 @@ import 'package:flutter/material.dart';
|
||||
|
||||
class ConversationsListScreen extends StatefulWidget {
|
||||
final bool useSmallFAB;
|
||||
final Function() onOpen;
|
||||
final Function()? onOpen;
|
||||
|
||||
const ConversationsListScreen({
|
||||
Key key,
|
||||
Key? key,
|
||||
this.useSmallFAB = false,
|
||||
this.onOpen,
|
||||
}) : assert(useSmallFAB != null),
|
||||
@ -37,9 +37,9 @@ class ConversationsListScreen extends StatefulWidget {
|
||||
class _ConversationScreenState extends SafeState<ConversationsListScreen> {
|
||||
final ConversationsHelper _conversationsHelper = ConversationsHelper();
|
||||
final UsersHelper _usersHelper = UsersHelper();
|
||||
ConversationsList _list;
|
||||
UsersList _users;
|
||||
GroupsList _groups;
|
||||
ConversationsList? _list;
|
||||
late UsersList _users;
|
||||
GroupsList? _groups;
|
||||
LoadErrorLevel _error = LoadErrorLevel.NONE;
|
||||
final GlobalKey<RefreshIndicatorState> _refreshIndicatorKey =
|
||||
GlobalKey<RefreshIndicatorState>();
|
||||
@ -49,7 +49,7 @@ class _ConversationScreenState extends SafeState<ConversationsListScreen> {
|
||||
super.initState();
|
||||
|
||||
this.listen<NewNumberUnreadConversations>(
|
||||
(d) => _refreshIndicatorKey.currentState.show());
|
||||
(d) => _refreshIndicatorKey.currentState!.show());
|
||||
}
|
||||
|
||||
@override
|
||||
@ -94,9 +94,9 @@ class _ConversationScreenState extends SafeState<ConversationsListScreen> {
|
||||
tr("Could not retrieve the list of conversations!"),
|
||||
actions: <Widget>[
|
||||
TextButton(
|
||||
onPressed: () => _refreshIndicatorKey.currentState.show(),
|
||||
onPressed: () => _refreshIndicatorKey.currentState!.show(),
|
||||
child: Text(
|
||||
tr("Retry").toUpperCase(),
|
||||
tr("Retry")!.toUpperCase(),
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
),
|
||||
@ -108,23 +108,23 @@ class _ConversationScreenState extends SafeState<ConversationsListScreen> {
|
||||
|
||||
/// Open a conversation
|
||||
void _openConversation(Conversation conv) {
|
||||
MainController.of(context).openConversation(conv);
|
||||
if (widget.onOpen != null) widget.onOpen();
|
||||
MainController.of(context)!.openConversation(conv);
|
||||
if (widget.onOpen != null) widget.onOpen!();
|
||||
}
|
||||
|
||||
/// Create a new conversation
|
||||
void _createConversation() {
|
||||
MainController.of(context).push(
|
||||
MainController.of(context)!.push(
|
||||
CreateConversationScreen(),
|
||||
canShowAsDialog: true,
|
||||
hideNavBar: true,
|
||||
);
|
||||
if (widget.onOpen != null) widget.onOpen();
|
||||
if (widget.onOpen != null) widget.onOpen!();
|
||||
}
|
||||
|
||||
/// Handle conversations updated requests
|
||||
void _updateConversation(Conversation conversation) {
|
||||
MainController.of(context).openConversationSettingsRoute(conversation);
|
||||
MainController.of(context)!.openConversationSettingsRoute(conversation);
|
||||
}
|
||||
|
||||
/// Handle conversation deletion request
|
||||
@ -153,7 +153,7 @@ class _ConversationScreenState extends SafeState<ConversationsListScreen> {
|
||||
} catch (e, s) {
|
||||
print("Failed to leave conversation! $e => $s");
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(tr("Could not leave the conversation!"))));
|
||||
SnackBar(content: Text(tr("Could not leave the conversation!")!)));
|
||||
}
|
||||
|
||||
// Reload the list of conversations
|
||||
@ -181,11 +181,11 @@ class _ConversationScreenState extends SafeState<ConversationsListScreen> {
|
||||
physics: AlwaysScrollableScrollPhysics(),
|
||||
controller: ScrollController(),
|
||||
itemBuilder: (context, index) {
|
||||
if (_list[index].isGroupConversation &&
|
||||
!_list[index].following) return Container();
|
||||
if (_list![index].isGroupConversation &&
|
||||
!_list![index].following) return Container();
|
||||
|
||||
return ConversationTile(
|
||||
conversation: _list.elementAt(index),
|
||||
conversation: _list!.elementAt(index),
|
||||
usersList: _users,
|
||||
groupsList: _groups,
|
||||
onOpen: (c) {
|
||||
@ -195,7 +195,7 @@ class _ConversationScreenState extends SafeState<ConversationsListScreen> {
|
||||
onRequestLeave: _requestLeaveConversation,
|
||||
);
|
||||
},
|
||||
itemCount: _list.length,
|
||||
itemCount: _list!.length,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -22,7 +22,7 @@ enum _ErrorsLevel { NONE, MINOR, MAJOR }
|
||||
class FriendsListScreen extends StatefulWidget {
|
||||
final bool showAppBar;
|
||||
|
||||
const FriendsListScreen({Key key, this.showAppBar = true}) : super(key: key);
|
||||
const FriendsListScreen({Key? key, this.showAppBar = true}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _FriendsListScreenState();
|
||||
@ -35,8 +35,8 @@ class _FriendsListScreenState extends SafeState<FriendsListScreen> {
|
||||
|
||||
/// Widget members
|
||||
_ErrorsLevel _error = _ErrorsLevel.NONE;
|
||||
FriendsList _friendsList;
|
||||
UsersList _usersInfo;
|
||||
FriendsList? _friendsList;
|
||||
late UsersList _usersInfo;
|
||||
GlobalKey<RefreshIndicatorState> _refreshIndicatorKey =
|
||||
GlobalKey<RefreshIndicatorState>();
|
||||
|
||||
@ -61,7 +61,7 @@ class _FriendsListScreenState extends SafeState<FriendsListScreen> {
|
||||
|
||||
/// Refresh the list of friends
|
||||
Future<void> _refreshList() async {
|
||||
await _refreshIndicatorKey.currentState.show();
|
||||
await _refreshIndicatorKey.currentState!.show();
|
||||
}
|
||||
|
||||
/// Load the list of friends
|
||||
@ -72,7 +72,7 @@ class _FriendsListScreenState extends SafeState<FriendsListScreen> {
|
||||
final list = await _friendsHelper.getList(online: online);
|
||||
|
||||
// Check if there is no cache yet
|
||||
if (!online && list.isEmpty) return;
|
||||
if (!online && list!.isEmpty) return;
|
||||
|
||||
// Check for errors
|
||||
if (list == null) return _gotError();
|
||||
@ -98,7 +98,7 @@ class _FriendsListScreenState extends SafeState<FriendsListScreen> {
|
||||
TextButton(
|
||||
onPressed: _refreshList,
|
||||
child: Text(
|
||||
tr("Retry").toUpperCase(),
|
||||
tr("Retry")!.toUpperCase(),
|
||||
style: TextStyle(color: Colors.white),
|
||||
),
|
||||
),
|
||||
@ -109,7 +109,7 @@ class _FriendsListScreenState extends SafeState<FriendsListScreen> {
|
||||
Widget build(BuildContext context) => Scaffold(
|
||||
appBar: widget.showAppBar
|
||||
? AppBar(
|
||||
title: Text(tr("Your friends list")),
|
||||
title: Text(tr("Your friends list")!),
|
||||
)
|
||||
: null,
|
||||
body: _buildBody(),
|
||||
@ -131,18 +131,18 @@ class _FriendsListScreenState extends SafeState<FriendsListScreen> {
|
||||
onRefresh: () => _loadList(true),
|
||||
child: ListView.builder(
|
||||
physics: AlwaysScrollableScrollPhysics(),
|
||||
itemCount: _friendsList.length,
|
||||
itemBuilder: (c, i) => _friendsList[i].accepted
|
||||
itemCount: _friendsList!.length,
|
||||
itemBuilder: (c, i) => _friendsList![i].accepted
|
||||
? AcceptedFriendTile(
|
||||
friend: _friendsList[i],
|
||||
user: _usersInfo.getUser(_friendsList[i].id),
|
||||
friend: _friendsList![i],
|
||||
user: _usersInfo.getUser(_friendsList![i].id),
|
||||
onOpenPrivateConversation: _openPrivateConversation,
|
||||
onSetFollowing: _setFollowingFriend,
|
||||
onRequestDelete: _deleteFriend,
|
||||
)
|
||||
: PendingFriendTile(
|
||||
friend: _friendsList[i],
|
||||
user: _usersInfo.getUser(_friendsList[i].id),
|
||||
friend: _friendsList![i],
|
||||
user: _usersInfo.getUser(_friendsList![i].id),
|
||||
onRespond: _respondRequest,
|
||||
)),
|
||||
),
|
||||
@ -154,7 +154,7 @@ class _FriendsListScreenState extends SafeState<FriendsListScreen> {
|
||||
/// Respond to friendship request
|
||||
Future<void> _respondRequest(Friend f, bool accept) async {
|
||||
if (!await _friendsHelper.respondRequest(f.id, accept))
|
||||
showSimpleSnack(context, tr("Could not respond to friendship request!"));
|
||||
showSimpleSnack(context, tr("Could not respond to friendship request!")!);
|
||||
|
||||
// Load the list of friends again
|
||||
_refreshList();
|
||||
@ -163,7 +163,7 @@ class _FriendsListScreenState extends SafeState<FriendsListScreen> {
|
||||
/// Update following status of a friend
|
||||
Future<void> _setFollowingFriend(Friend friend, bool follow) async {
|
||||
if (!await _friendsHelper.setFollowing(friend.id, follow))
|
||||
showSimpleSnack(context, tr("Could not update following status!"));
|
||||
showSimpleSnack(context, tr("Could not update following status!")!);
|
||||
|
||||
_refreshList();
|
||||
}
|
||||
@ -173,18 +173,18 @@ class _FriendsListScreenState extends SafeState<FriendsListScreen> {
|
||||
final choice = await showDialog<bool>(
|
||||
context: context,
|
||||
builder: (b) => AlertDialog(
|
||||
title: Text(tr("Delete friend")),
|
||||
title: Text(tr("Delete friend")!),
|
||||
content: Text(tr(
|
||||
"Are you sure do you want to remove this friend from your list of friends ? A friendship request will have to be sent to get this user back to your list!")),
|
||||
"Are you sure do you want to remove this friend from your list of friends ? A friendship request will have to be sent to get this user back to your list!")!),
|
||||
actions: <Widget>[
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context, false),
|
||||
child: Text(tr("Cancel").toUpperCase()),
|
||||
child: Text(tr("Cancel")!.toUpperCase()),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context, true),
|
||||
child: Text(
|
||||
tr("Confirm").toUpperCase(),
|
||||
tr("Confirm")!.toUpperCase(),
|
||||
style: TextStyle(color: Colors.red),
|
||||
),
|
||||
),
|
||||
@ -197,7 +197,7 @@ class _FriendsListScreenState extends SafeState<FriendsListScreen> {
|
||||
// Forward the request to the server
|
||||
if (!await _friendsHelper.removeFriend(f.id))
|
||||
showSimpleSnack(
|
||||
context, tr("Could not delete this person from your friends list!"));
|
||||
context, tr("Could not delete this person from your friends list!")!);
|
||||
|
||||
// Refresh list
|
||||
_refreshList();
|
||||
|
@ -16,9 +16,9 @@ class GroupAccessDeniedScreen extends StatefulWidget {
|
||||
final Function() onMembershipAcquired;
|
||||
|
||||
const GroupAccessDeniedScreen({
|
||||
Key key,
|
||||
@required this.groupID,
|
||||
@required this.onMembershipAcquired,
|
||||
Key? key,
|
||||
required this.groupID,
|
||||
required this.onMembershipAcquired,
|
||||
}) : assert(groupID != null),
|
||||
assert(onMembershipAcquired != null),
|
||||
super(key: key);
|
||||
@ -29,7 +29,7 @@ class GroupAccessDeniedScreen extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _GroupAccessDeniedScreenState extends SafeState<GroupAccessDeniedScreen> {
|
||||
Group _group;
|
||||
Group? _group;
|
||||
|
||||
bool error = false;
|
||||
|
||||
@ -45,7 +45,7 @@ class _GroupAccessDeniedScreenState extends SafeState<GroupAccessDeniedScreen> {
|
||||
return buildErrorCard(tr("Could not get basic group information!"),
|
||||
actions: [
|
||||
MaterialButton(
|
||||
child: Text(tr("Try again").toUpperCase()),
|
||||
child: Text(tr("Try again")!.toUpperCase()),
|
||||
onPressed: () => this._refresh(),
|
||||
)
|
||||
]);
|
||||
@ -61,20 +61,20 @@ class _GroupAccessDeniedScreenState extends SafeState<GroupAccessDeniedScreen> {
|
||||
Spacer(
|
||||
flex: 5,
|
||||
),
|
||||
GroupIcon(group: _group),
|
||||
GroupIcon(group: _group!),
|
||||
Spacer(),
|
||||
Text(
|
||||
_group.displayName,
|
||||
_group!.displayName,
|
||||
style: TextStyle(fontSize: 20),
|
||||
),
|
||||
Spacer(),
|
||||
Text(
|
||||
tr("A registration is required to access this group page."),
|
||||
tr("A registration is required to access this group page.")!,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
Spacer(),
|
||||
GroupMembershipWidget(
|
||||
group: _group,
|
||||
group: _group!,
|
||||
onUpdated: () => this._refresh(),
|
||||
onError: () => this._refresh(),
|
||||
),
|
||||
|
@ -12,11 +12,11 @@ import 'package:flutter/material.dart';
|
||||
|
||||
class GroupPageScreen extends StatefulWidget {
|
||||
final int groupID;
|
||||
final int conversationID;
|
||||
final int? conversationID;
|
||||
|
||||
const GroupPageScreen({
|
||||
Key key,
|
||||
@required this.groupID,
|
||||
Key? key,
|
||||
required this.groupID,
|
||||
this.conversationID,
|
||||
}) : assert(groupID != null),
|
||||
super(key: key);
|
||||
@ -28,7 +28,7 @@ class GroupPageScreen extends StatefulWidget {
|
||||
class _GroupPageScreenState extends SafeState<GroupPageScreen> {
|
||||
int get groupID => widget.groupID;
|
||||
|
||||
GetAdvancedInfoResult _getGroupResult;
|
||||
GetAdvancedInfoResult? _getGroupResult;
|
||||
var error = false;
|
||||
|
||||
@override
|
||||
@ -44,7 +44,7 @@ class _GroupPageScreenState extends SafeState<GroupPageScreen> {
|
||||
actions: [
|
||||
MaterialButton(
|
||||
onPressed: () => _refreshPage(),
|
||||
child: Text(tr("Try again").toUpperCase()),
|
||||
child: Text(tr("Try again")!.toUpperCase()),
|
||||
)
|
||||
]);
|
||||
|
||||
@ -52,7 +52,7 @@ class _GroupPageScreenState extends SafeState<GroupPageScreen> {
|
||||
if (_getGroupResult == null) return buildCenteredProgressBar();
|
||||
|
||||
// Check if access to the group was denied
|
||||
if (_getGroupResult.status == GetAdvancedInfoStatus.ACCESS_DENIED)
|
||||
if (_getGroupResult!.status == GetAdvancedInfoStatus.ACCESS_DENIED)
|
||||
return GroupAccessDeniedScreen(
|
||||
groupID: groupID,
|
||||
onMembershipAcquired: () => _refreshPage(),
|
||||
@ -60,7 +60,7 @@ class _GroupPageScreenState extends SafeState<GroupPageScreen> {
|
||||
|
||||
// Now we can show group page
|
||||
return AuthorizedGroupPageScreen(
|
||||
advancedGroupInfo: _getGroupResult.info,
|
||||
advancedGroupInfo: _getGroupResult!.info!,
|
||||
conversationID: widget.conversationID,
|
||||
needRefresh: () => _refreshPage(),
|
||||
);
|
||||
|
@ -13,8 +13,8 @@ class AboutGroupSection extends StatelessWidget {
|
||||
final AdvancedGroupInfo group;
|
||||
|
||||
const AboutGroupSection({
|
||||
Key key,
|
||||
@required this.group,
|
||||
Key? key,
|
||||
required this.group,
|
||||
}) : assert(group != null),
|
||||
super(key: key);
|
||||
|
||||
@ -25,7 +25,7 @@ class AboutGroupSection extends StatelessWidget {
|
||||
group.hasURL
|
||||
? ListTile(
|
||||
leading: Icon(Icons.link),
|
||||
title: Text(tr("URL")),
|
||||
title: Text(tr("URL")!),
|
||||
subtitle: Text(group.url),
|
||||
onTap: () => launch(group.url),
|
||||
)
|
||||
@ -35,7 +35,7 @@ class AboutGroupSection extends StatelessWidget {
|
||||
group.hasDescription
|
||||
? ListTile(
|
||||
leading: Icon(Icons.note),
|
||||
title: Text(tr("Description")),
|
||||
title: Text(tr("Description")!),
|
||||
subtitle: Text(group.description),
|
||||
)
|
||||
: Container(),
|
||||
@ -44,64 +44,64 @@ class AboutGroupSection extends StatelessWidget {
|
||||
ListTile(
|
||||
leading: Icon(Icons.access_time),
|
||||
title: Text("Created"),
|
||||
subtitle: Text(diffTimeFromNowToStr(group.timeCreate)),
|
||||
subtitle: Text(diffTimeFromNowToStr(group.timeCreate!)!),
|
||||
),
|
||||
|
||||
// Number of members
|
||||
ListTile(
|
||||
leading: Icon(Icons.group),
|
||||
title: Text(tr("Members")),
|
||||
title: Text(tr("Members")!),
|
||||
subtitle: Text(
|
||||
tr("%1% members", args: {"1": group.numberMembers.toString()})),
|
||||
tr("%1% members", args: {"1": group.numberMembers.toString()})!),
|
||||
),
|
||||
|
||||
// Who can create posts
|
||||
ListTile(
|
||||
leading: Icon(Icons.add),
|
||||
title: Text(tr("Who can create posts")),
|
||||
title: Text(tr("Who can create posts")!),
|
||||
subtitle: Text(
|
||||
group.postCreationLevel == GroupPostCreationLevel.MEMBERS
|
||||
? tr("Every members")
|
||||
: tr("Only moderators and administrators")),
|
||||
? tr("Every members")!
|
||||
: tr("Only moderators and administrators")!),
|
||||
),
|
||||
|
||||
// Registration process
|
||||
ListTile(
|
||||
leading: Icon(Icons.login),
|
||||
title: Text(tr("Registration process")),
|
||||
title: Text(tr("Registration process")!),
|
||||
subtitle: Text(group.registrationLevel ==
|
||||
GroupRegistrationLevel.CLOSED
|
||||
? tr("On invitation only")
|
||||
? tr("On invitation only")!
|
||||
: (group.registrationLevel == GroupRegistrationLevel.MODERATED
|
||||
? tr("A moderator has to approve requests")
|
||||
: tr("Anyone can join the group without approval"))),
|
||||
? tr("A moderator has to approve requests")!
|
||||
: tr("Anyone can join the group without approval")!)),
|
||||
),
|
||||
|
||||
// Group visibility
|
||||
ListTile(
|
||||
leading: Icon(Icons.remove_red_eye),
|
||||
title: Text(tr("Visibility")),
|
||||
title: Text(tr("Visibility")!),
|
||||
subtitle: Text(group.visibilityLevel == GroupVisibilityLevel.SECRETE
|
||||
? tr("Secrete group")
|
||||
? tr("Secrete group")!
|
||||
: (group.visibilityLevel == GroupVisibilityLevel.PRIVATE
|
||||
? tr("Private group")
|
||||
: tr("Open group"))),
|
||||
? tr("Private group")!
|
||||
: tr("Open group")!)),
|
||||
),
|
||||
|
||||
// Group members visibility
|
||||
ListTile(
|
||||
leading: Icon(Icons.remove_red_eye),
|
||||
title: Text(tr("Members list visibility")),
|
||||
title: Text(tr("Members list visibility")!),
|
||||
subtitle:
|
||||
Text(group.isMembersListPublic ? tr("Public") : tr("Private")),
|
||||
Text(group.isMembersListPublic! ? tr("Public")! : tr("Private")!),
|
||||
),
|
||||
|
||||
group.isForezGroup
|
||||
? // Group members visibility
|
||||
ListTile(
|
||||
leading: Icon(Icons.info_outline),
|
||||
title: Text(tr("Forez group")),
|
||||
subtitle: Text(tr("Forez special features enabled")),
|
||||
title: Text(tr("Forez group")!),
|
||||
subtitle: Text(tr("Forez special features enabled")!),
|
||||
)
|
||||
: Container(),
|
||||
],
|
||||
|
@ -16,8 +16,8 @@ class ForezPresenceSection extends StatefulWidget {
|
||||
final int groupID;
|
||||
|
||||
const ForezPresenceSection({
|
||||
Key key,
|
||||
@required this.groupID,
|
||||
Key? key,
|
||||
required this.groupID,
|
||||
}) : assert(groupID != null),
|
||||
super(key: key);
|
||||
|
||||
@ -26,17 +26,17 @@ class ForezPresenceSection extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _ForezPresenceSectionState extends State<ForezPresenceSection> {
|
||||
PresenceSet _presences;
|
||||
UsersList _users;
|
||||
PresenceSet? _presences;
|
||||
late UsersList _users;
|
||||
DateTime _currentDay = DateTime.now();
|
||||
|
||||
List<int> get _currentListOfUsers => _presences.getUsersAtDate(_currentDay);
|
||||
List<int> get _currentListOfUsers => _presences!.getUsersAtDate(_currentDay);
|
||||
|
||||
Future<void> _refresh() async {
|
||||
await ForezPresenceHelper.refreshCache(widget.groupID);
|
||||
|
||||
_presences = await ForezPresenceHelper.getAll(widget.groupID);
|
||||
_users = await UsersHelper().getList(_presences.usersID);
|
||||
_users = await UsersHelper().getList(_presences!.usersID);
|
||||
}
|
||||
|
||||
@override
|
||||
@ -71,7 +71,7 @@ class _ForezPresenceSectionState extends State<ForezPresenceSection> {
|
||||
}
|
||||
|
||||
Widget _buildCalendar() => PresenceCalendarWidget(
|
||||
presenceSet: _presences,
|
||||
presenceSet: _presences!,
|
||||
mode: CalendarDisplayMode.MULTIPLE_USERS,
|
||||
selectedDay: _currentDay,
|
||||
onDayClicked: _selectDay,
|
||||
|
@ -12,8 +12,8 @@ class GroupConversationSection extends StatelessWidget {
|
||||
final Conversation conv;
|
||||
|
||||
const GroupConversationSection({
|
||||
Key key,
|
||||
@required this.conv,
|
||||
Key? key,
|
||||
required this.conv,
|
||||
}) : assert(conv != null),
|
||||
super(key: key);
|
||||
|
||||
@ -21,7 +21,7 @@ class GroupConversationSection extends StatelessWidget {
|
||||
Widget build(BuildContext context) => Stack(
|
||||
children: [
|
||||
ConversationScreen(
|
||||
conversationID: conv.id,
|
||||
conversationID: conv.id!,
|
||||
),
|
||||
Positioned(
|
||||
right: 1.0,
|
||||
@ -31,12 +31,12 @@ class GroupConversationSection extends StatelessWidget {
|
||||
? IconButton(
|
||||
icon: Icon(Icons.phone),
|
||||
onPressed: () =>
|
||||
MainController.of(context).startCall(conv.id),
|
||||
MainController.of(context)!.startCall(conv.id!),
|
||||
)
|
||||
: Container(),
|
||||
IconButton(
|
||||
icon: Icon(Icons.settings),
|
||||
onPressed: () => MainController.of(context)
|
||||
onPressed: () => MainController.of(context)!
|
||||
.openConversationSettingsRoute(conv),
|
||||
),
|
||||
],
|
||||
|
@ -21,7 +21,7 @@ import 'package:flutter/material.dart';
|
||||
class GroupMembersSection extends StatefulWidget {
|
||||
final int groupID;
|
||||
|
||||
const GroupMembersSection({Key key, this.groupID})
|
||||
const GroupMembersSection({Key? key, required this.groupID})
|
||||
: assert(groupID != null),
|
||||
super(key: key);
|
||||
|
||||
@ -31,9 +31,9 @@ class GroupMembersSection extends StatefulWidget {
|
||||
|
||||
class _GroupMembersSectionState extends State<GroupMembersSection> {
|
||||
final _key = GlobalKey<AsyncScreenWidgetState>();
|
||||
Group _group;
|
||||
GroupMembersList _members;
|
||||
UsersList _users;
|
||||
late Group _group;
|
||||
late GroupMembersList _members;
|
||||
late UsersList _users;
|
||||
|
||||
Future<void> _refresh() async {
|
||||
final group = await GroupsHelper().getSingle(widget.groupID, force: true);
|
||||
@ -50,7 +50,7 @@ class _GroupMembersSectionState extends State<GroupMembersSection> {
|
||||
key: _key,
|
||||
onReload: _refresh,
|
||||
onBuild: _buildBodyContent,
|
||||
errorMessage: tr("Could not load the list of members of this group!"),
|
||||
errorMessage: tr("Could not load the list of members of this group!")!,
|
||||
showOldDataWhileUpdating: true,
|
||||
);
|
||||
|
||||
@ -70,7 +70,7 @@ class _GroupMembersSectionState extends State<GroupMembersSection> {
|
||||
group: _group,
|
||||
membership: membership,
|
||||
user: user,
|
||||
onUpdated: () => _key.currentState.refresh(),
|
||||
onUpdated: () => _key.currentState!.refresh(),
|
||||
);
|
||||
}
|
||||
|
||||
@ -93,40 +93,40 @@ class _GroupMembersSectionState extends State<GroupMembersSection> {
|
||||
await GroupsHelper.sendInvitation(widget.groupID, userID);
|
||||
} catch (e, s) {
|
||||
print("Could not invite a user! $e\n$s");
|
||||
showSimpleSnack(context, tr("Could not invite a user!"));
|
||||
showSimpleSnack(context, tr("Could not invite a user!")!);
|
||||
}
|
||||
|
||||
_key.currentState.refresh();
|
||||
_key.currentState!.refresh();
|
||||
}
|
||||
}
|
||||
|
||||
List<MultiChoiceEntry<GroupMembershipLevel>> get _membershipLevels => [
|
||||
MultiChoiceEntry(
|
||||
id: GroupMembershipLevel.ADMINISTRATOR,
|
||||
title: tr("Administrator"),
|
||||
title: tr("Administrator")!,
|
||||
subtitle: tr("Can change members privileges and group settings")),
|
||||
MultiChoiceEntry(
|
||||
id: GroupMembershipLevel.MODERATOR,
|
||||
title: tr("Moderator"),
|
||||
title: tr("Moderator")!,
|
||||
subtitle: tr(
|
||||
"Can always create posts, invite users and respond to membership request")),
|
||||
MultiChoiceEntry(
|
||||
id: GroupMembershipLevel.MEMBER,
|
||||
title: tr("Member"),
|
||||
title: tr("Member")!,
|
||||
subtitle: tr("Can access to all group posts")),
|
||||
MultiChoiceEntry(
|
||||
id: GroupMembershipLevel.PENDING,
|
||||
title: tr("Requested"),
|
||||
title: tr("Requested")!,
|
||||
hidden: true,
|
||||
),
|
||||
MultiChoiceEntry(
|
||||
id: GroupMembershipLevel.INVITED,
|
||||
title: tr("Invited"),
|
||||
title: tr("Invited")!,
|
||||
hidden: true,
|
||||
),
|
||||
MultiChoiceEntry(
|
||||
id: GroupMembershipLevel.VISITOR,
|
||||
title: tr("Visitor"),
|
||||
title: tr("Visitor")!,
|
||||
hidden: true,
|
||||
),
|
||||
];
|
||||
@ -138,11 +138,11 @@ class _GroupMembershipTile extends StatefulWidget {
|
||||
final void Function() onUpdated;
|
||||
|
||||
const _GroupMembershipTile({
|
||||
Key key,
|
||||
@required this.group,
|
||||
@required this.membership,
|
||||
@required this.user,
|
||||
@required this.onUpdated,
|
||||
Key? key,
|
||||
required this.group,
|
||||
required this.membership,
|
||||
required this.user,
|
||||
required this.onUpdated,
|
||||
}) : assert(group != null),
|
||||
assert(membership != null),
|
||||
assert(user != null),
|
||||
@ -176,7 +176,7 @@ class __GroupMembershipTileState extends State<_GroupMembershipTile> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildTrailing() {
|
||||
Widget? _buildTrailing() {
|
||||
switch (widget.membership.level) {
|
||||
case GroupMembershipLevel.ADMINISTRATOR:
|
||||
case GroupMembershipLevel.MODERATOR:
|
||||
@ -205,14 +205,14 @@ class __GroupMembershipTileState extends State<_GroupMembershipTile> {
|
||||
itemBuilder: (c) => [
|
||||
// Change membership level
|
||||
PopupMenuItem(
|
||||
child: Text(tr("Change level")),
|
||||
child: Text(tr("Change level")!),
|
||||
enabled: !_isCurrentUser && widget.group.isAdmin,
|
||||
value: _MemberMenuOptions.CHANGE_LEVEL,
|
||||
),
|
||||
|
||||
// Remove membership
|
||||
PopupMenuItem(
|
||||
child: Text(tr("Remove")),
|
||||
child: Text(tr("Remove")!),
|
||||
value: _MemberMenuOptions.DELETE,
|
||||
enabled: !_isCurrentUser,
|
||||
),
|
||||
@ -247,7 +247,7 @@ class __GroupMembershipTileState extends State<_GroupMembershipTile> {
|
||||
await GroupsHelper.setNewLevel(groupID, memberID, newLevel);
|
||||
} catch (e, s) {
|
||||
print("Could not change group membership level! $e\n$s");
|
||||
showSimpleSnack(context, tr("Could not change group membership level!"));
|
||||
showSimpleSnack(context, tr("Could not change group membership level!")!);
|
||||
}
|
||||
|
||||
widget.onUpdated();
|
||||
@ -263,7 +263,7 @@ class __GroupMembershipTileState extends State<_GroupMembershipTile> {
|
||||
await GroupsHelper.removeMemberFromGroup(groupID, memberID);
|
||||
} catch (e, s) {
|
||||
print("Could not remove membership! $e\n$s");
|
||||
showSimpleSnack(context, tr("Could not remove this membership!"));
|
||||
showSimpleSnack(context, tr("Could not remove this membership!")!);
|
||||
}
|
||||
|
||||
widget.onUpdated();
|
||||
@ -272,7 +272,7 @@ class __GroupMembershipTileState extends State<_GroupMembershipTile> {
|
||||
Widget _buildInvitedCase() {
|
||||
return MaterialButton(
|
||||
onPressed: _cancelMembership,
|
||||
child: Text(tr("Cancel").toUpperCase()),
|
||||
child: Text(tr("Cancel")!.toUpperCase()),
|
||||
textColor: Colors.red,
|
||||
);
|
||||
}
|
||||
@ -282,7 +282,7 @@ class __GroupMembershipTileState extends State<_GroupMembershipTile> {
|
||||
await GroupsHelper.cancelInvitation(groupID, memberID);
|
||||
} catch (e, s) {
|
||||
print("Could not cancel invitation! $e\n$s");
|
||||
showSimpleSnack(context, tr("Could not cancel invitation!"));
|
||||
showSimpleSnack(context, tr("Could not cancel invitation!")!);
|
||||
}
|
||||
|
||||
widget.onUpdated();
|
||||
@ -294,12 +294,12 @@ class __GroupMembershipTileState extends State<_GroupMembershipTile> {
|
||||
children: <Widget>[
|
||||
MaterialButton(
|
||||
onPressed: () => _respondRequest(false),
|
||||
child: Text(tr("Reject").toUpperCase()),
|
||||
child: Text(tr("Reject")!.toUpperCase()),
|
||||
textColor: Colors.red,
|
||||
),
|
||||
MaterialButton(
|
||||
onPressed: () => _respondRequest(true),
|
||||
child: Text(tr("Accept").toUpperCase()),
|
||||
child: Text(tr("Accept")!.toUpperCase()),
|
||||
textColor: Colors.green,
|
||||
)
|
||||
],
|
||||
@ -311,7 +311,7 @@ class __GroupMembershipTileState extends State<_GroupMembershipTile> {
|
||||
await GroupsHelper.respondRequest(groupID, memberID, accept);
|
||||
} catch (e, s) {
|
||||
print("Could not respond to membership request! $e\n$s");
|
||||
showSimpleSnack(context, tr("Could not respond to membership request!"));
|
||||
showSimpleSnack(context, tr("Could not respond to membership request!")!);
|
||||
}
|
||||
|
||||
widget.onUpdated();
|
||||
|
@ -12,8 +12,8 @@ class GroupPostsSection extends StatefulWidget {
|
||||
final AdvancedGroupInfo group;
|
||||
|
||||
const GroupPostsSection({
|
||||
Key key,
|
||||
@required this.group,
|
||||
Key? key,
|
||||
required this.group,
|
||||
}) : assert(group != null),
|
||||
super(key: key);
|
||||
|
||||
@ -31,7 +31,7 @@ class _GroupPostsSectionState extends State<GroupPostsSection> {
|
||||
return PostCreateFormWidget(
|
||||
postTarget: PostTarget.GROUP_PAGE,
|
||||
targetID: widget.group.id,
|
||||
onCreated: () => _postsKey.currentState.loadPostsList(getOlder: false),
|
||||
onCreated: () => _postsKey.currentState!.loadPostsList(getOlder: false),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -34,7 +34,7 @@ import 'package:flutter_settings_ui/flutter_settings_ui.dart';
|
||||
class GroupSettingsScreen extends StatefulWidget {
|
||||
final int groupID;
|
||||
|
||||
const GroupSettingsScreen({Key key, @required this.groupID})
|
||||
const GroupSettingsScreen({Key? key, required this.groupID})
|
||||
: assert(groupID != null),
|
||||
super(key: key);
|
||||
|
||||
@ -43,7 +43,7 @@ class GroupSettingsScreen extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _GroupSettingsScreenState extends SafeState<GroupSettingsScreen> {
|
||||
AdvancedGroupInfo _groupSettings;
|
||||
AdvancedGroupInfo? _groupSettings;
|
||||
|
||||
final _key = GlobalKey<AsyncScreenWidgetState>();
|
||||
|
||||
@ -53,13 +53,13 @@ class _GroupSettingsScreenState extends SafeState<GroupSettingsScreen> {
|
||||
|
||||
Future<void> _updateSettings() async {
|
||||
try {
|
||||
await GroupsHelper.setSettings(_groupSettings);
|
||||
await GroupsHelper.setSettings(_groupSettings!);
|
||||
} catch (e, stack) {
|
||||
print("Could not update group settings! $e\n$stack");
|
||||
showSimpleSnack(context, tr("Could not update group settings!"));
|
||||
showSimpleSnack(context, tr("Could not update group settings!")!);
|
||||
}
|
||||
|
||||
_key.currentState.refresh();
|
||||
_key.currentState!.refresh();
|
||||
}
|
||||
|
||||
@override
|
||||
@ -67,7 +67,7 @@ class _GroupSettingsScreenState extends SafeState<GroupSettingsScreen> {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
leading: ComunicBackButton(),
|
||||
title: Text(tr("Group settings")),
|
||||
title: Text(tr("Group settings")!),
|
||||
),
|
||||
body: _buildBody(),
|
||||
);
|
||||
@ -78,7 +78,7 @@ class _GroupSettingsScreenState extends SafeState<GroupSettingsScreen> {
|
||||
key: _key,
|
||||
onReload: _refresh,
|
||||
onBuild: _buildContent,
|
||||
errorMessage: tr("Could not get group settings!"),
|
||||
errorMessage: tr("Could not get group settings!")!,
|
||||
showOldDataWhileUpdating: true,
|
||||
);
|
||||
}
|
||||
@ -96,65 +96,65 @@ class _GroupSettingsScreenState extends SafeState<GroupSettingsScreen> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildGeneralSection() {
|
||||
SettingsSection _buildGeneralSection() {
|
||||
return SettingsSection(
|
||||
title: tr("General information"),
|
||||
tiles: [
|
||||
// Group ID
|
||||
SettingsTile(
|
||||
title: tr("Group ID"),
|
||||
subtitle: _groupSettings.id.toString(),
|
||||
subtitle: _groupSettings!.id.toString(),
|
||||
),
|
||||
|
||||
// Group name
|
||||
TextEditSettingsTile(
|
||||
title: tr("Group name"),
|
||||
currValue: _groupSettings.name,
|
||||
title: tr("Group name")!,
|
||||
currValue: _groupSettings!.name,
|
||||
onChanged: (s) {
|
||||
_groupSettings.name = s;
|
||||
_groupSettings!.name = s;
|
||||
_updateSettings();
|
||||
}),
|
||||
|
||||
// Group virtual directory
|
||||
SettingsTile(
|
||||
title: tr("Virtual directory (optional)"),
|
||||
subtitle: _groupSettings.virtualDirectory,
|
||||
subtitle: _groupSettings!.virtualDirectory,
|
||||
onPressed: (_) async {
|
||||
final newDir = await showVirtualDirectoryDialog(
|
||||
context: context,
|
||||
initialDirectory: _groupSettings.virtualDirectory,
|
||||
id: _groupSettings.id,
|
||||
initialDirectory: _groupSettings!.virtualDirectory,
|
||||
id: _groupSettings!.id,
|
||||
type: VirtualDirectoryTargetType.GROUP,
|
||||
);
|
||||
|
||||
if (newDir == null) return;
|
||||
|
||||
_groupSettings.virtualDirectory = newDir;
|
||||
_groupSettings!.virtualDirectory = newDir;
|
||||
_updateSettings();
|
||||
},
|
||||
),
|
||||
|
||||
// Group URL
|
||||
TextEditSettingsTile(
|
||||
title: tr("Group URL (optional)"),
|
||||
currValue: _groupSettings.url,
|
||||
title: tr("Group URL (optional)")!,
|
||||
currValue: _groupSettings!.url,
|
||||
checkInput: validateUrl,
|
||||
allowEmptyValues: true,
|
||||
onChanged: (s) {
|
||||
_groupSettings.url = s;
|
||||
_groupSettings!.url = s;
|
||||
_updateSettings();
|
||||
},
|
||||
),
|
||||
|
||||
// Group description
|
||||
TextEditSettingsTile(
|
||||
title: tr("Group description (optional)"),
|
||||
currValue: _groupSettings.description,
|
||||
title: tr("Group description (optional)")!,
|
||||
currValue: _groupSettings!.description,
|
||||
maxLines: 3,
|
||||
maxLength: 255,
|
||||
allowEmptyValues: true,
|
||||
onChanged: (s) {
|
||||
_groupSettings.description = s;
|
||||
_groupSettings!.description = s;
|
||||
_updateSettings();
|
||||
}),
|
||||
],
|
||||
@ -164,18 +164,18 @@ class _GroupSettingsScreenState extends SafeState<GroupSettingsScreen> {
|
||||
List<MultiChoiceEntry<GroupVisibilityLevel>> get _visibilityLevels => [
|
||||
MultiChoiceEntry(
|
||||
id: GroupVisibilityLevel.OPEN,
|
||||
title: tr("Open group"),
|
||||
title: tr("Open group")!,
|
||||
subtitle:
|
||||
tr("Group information & public posts are available to everyone."),
|
||||
),
|
||||
MultiChoiceEntry(
|
||||
id: GroupVisibilityLevel.PRIVATE,
|
||||
title: tr("Private group"),
|
||||
title: tr("Private group")!,
|
||||
subtitle: tr("The group is accessible to accepted members only."),
|
||||
),
|
||||
MultiChoiceEntry(
|
||||
id: GroupVisibilityLevel.SECRETE,
|
||||
title: tr("Secrete group"),
|
||||
title: tr("Secrete group")!,
|
||||
subtitle: tr("The group is visible only to invited members."),
|
||||
),
|
||||
];
|
||||
@ -183,19 +183,19 @@ class _GroupSettingsScreenState extends SafeState<GroupSettingsScreen> {
|
||||
List<MultiChoiceEntry<GroupRegistrationLevel>> get _registrationLevels => [
|
||||
MultiChoiceEntry(
|
||||
id: GroupRegistrationLevel.OPEN,
|
||||
title: tr("Open registration"),
|
||||
title: tr("Open registration")!,
|
||||
subtitle: tr(
|
||||
"Everyone can choose to join the group without moderator approval"),
|
||||
),
|
||||
MultiChoiceEntry(
|
||||
id: GroupRegistrationLevel.MODERATED,
|
||||
title: tr("Moderated registration"),
|
||||
title: tr("Moderated registration")!,
|
||||
subtitle: tr(
|
||||
"Everyone can request a membership, but a moderator review the request"),
|
||||
),
|
||||
MultiChoiceEntry(
|
||||
id: GroupRegistrationLevel.CLOSED,
|
||||
title: tr("Closed registration"),
|
||||
title: tr("Closed registration")!,
|
||||
subtitle: tr(
|
||||
"The only way to join the group is to be invited by a moderator"),
|
||||
),
|
||||
@ -204,48 +204,48 @@ class _GroupSettingsScreenState extends SafeState<GroupSettingsScreen> {
|
||||
List<MultiChoiceEntry<GroupPostCreationLevel>> get _postsCreationLevels => [
|
||||
MultiChoiceEntry(
|
||||
id: GroupPostCreationLevel.MEMBERS,
|
||||
title: tr("All members"),
|
||||
title: tr("All members")!,
|
||||
subtitle:
|
||||
tr("All the members of the group can create posts on the group"),
|
||||
),
|
||||
MultiChoiceEntry(
|
||||
id: GroupPostCreationLevel.MODERATORS,
|
||||
title: tr("Moderators only"),
|
||||
title: tr("Moderators only")!,
|
||||
subtitle: tr(
|
||||
"Only moderators and administrators of the group can create posts on it"),
|
||||
),
|
||||
];
|
||||
|
||||
Widget _buildAccessRestrictions() => SettingsSection(
|
||||
SettingsSection _buildAccessRestrictions() => SettingsSection(
|
||||
title: tr("Access restrictions"),
|
||||
tiles: [
|
||||
// Group visibility
|
||||
MultiChoicesSettingsTile(
|
||||
title: tr("Group visibility"),
|
||||
title: tr("Group visibility")!,
|
||||
choices: _visibilityLevels,
|
||||
currentValue: _groupSettings.visibilityLevel,
|
||||
onChanged: (v) {
|
||||
_groupSettings.visibilityLevel = v;
|
||||
currentValue: _groupSettings!.visibilityLevel,
|
||||
onChanged: (dynamic v) {
|
||||
_groupSettings!.visibilityLevel = v;
|
||||
_updateSettings();
|
||||
}),
|
||||
|
||||
// Group registration level
|
||||
MultiChoicesSettingsTile(
|
||||
title: tr("Group registration level"),
|
||||
title: tr("Group registration level")!,
|
||||
choices: _registrationLevels,
|
||||
currentValue: _groupSettings.registrationLevel,
|
||||
onChanged: (v) {
|
||||
_groupSettings.registrationLevel = v;
|
||||
currentValue: _groupSettings!.registrationLevel,
|
||||
onChanged: (dynamic v) {
|
||||
_groupSettings!.registrationLevel = v;
|
||||
_updateSettings();
|
||||
}),
|
||||
|
||||
// Group posts creation levels
|
||||
MultiChoicesSettingsTile(
|
||||
title: tr("Posts creation level"),
|
||||
title: tr("Posts creation level")!,
|
||||
choices: _postsCreationLevels,
|
||||
currentValue: _groupSettings.postCreationLevel,
|
||||
onChanged: (s) {
|
||||
_groupSettings.postCreationLevel = s;
|
||||
currentValue: _groupSettings!.postCreationLevel,
|
||||
onChanged: (dynamic s) {
|
||||
_groupSettings!.postCreationLevel = s;
|
||||
_updateSettings();
|
||||
}),
|
||||
|
||||
@ -253,10 +253,10 @@ class _GroupSettingsScreenState extends SafeState<GroupSettingsScreen> {
|
||||
SettingsTile.switchTile(
|
||||
title: tr("Make members list public"),
|
||||
onToggle: (s) {
|
||||
_groupSettings.isMembersListPublic = s;
|
||||
_groupSettings!.isMembersListPublic = s;
|
||||
_updateSettings();
|
||||
},
|
||||
switchValue: _groupSettings.isMembersListPublic,
|
||||
switchValue: _groupSettings!.isMembersListPublic,
|
||||
titleMaxLines: 2,
|
||||
)
|
||||
],
|
||||
@ -266,19 +266,19 @@ class _GroupSettingsScreenState extends SafeState<GroupSettingsScreen> {
|
||||
get _conversationMinMembershipLevel => [
|
||||
MultiChoiceEntry(
|
||||
id: GroupMembershipLevel.ADMINISTRATOR,
|
||||
title: tr("Administrators only"),
|
||||
title: tr("Administrators only")!,
|
||||
subtitle: tr(
|
||||
"Only the administrators of the group can access the conversation"),
|
||||
),
|
||||
MultiChoiceEntry(
|
||||
id: GroupMembershipLevel.MODERATOR,
|
||||
title: tr("Moderators and administrators"),
|
||||
title: tr("Moderators and administrators")!,
|
||||
subtitle: tr(
|
||||
"Only moderators and administrators of the group can access the conversation"),
|
||||
),
|
||||
MultiChoiceEntry(
|
||||
id: GroupMembershipLevel.MEMBER,
|
||||
title: tr("All members"),
|
||||
title: tr("All members")!,
|
||||
subtitle: tr(
|
||||
"All the members of the group can access the conversation"),
|
||||
),
|
||||
@ -286,17 +286,17 @@ class _GroupSettingsScreenState extends SafeState<GroupSettingsScreen> {
|
||||
|
||||
SettingsSection _buildConversationsArea() => SettingsSection(
|
||||
title: tr("Group conversations"),
|
||||
tiles: _groupSettings.conversations
|
||||
tiles: _groupSettings!.conversations!
|
||||
.map(
|
||||
(e) {
|
||||
SettingsTile tile =
|
||||
MultiChoicesSettingsTile<GroupMembershipLevel>(
|
||||
title: e.name,
|
||||
MultiChoicesSettingsTile<GroupMembershipLevel?>(
|
||||
title: e.name!,
|
||||
choices: _conversationMinMembershipLevel,
|
||||
currentValue: e.groupMinMembershipLevel,
|
||||
leading: e.hasLogo
|
||||
? CachedNetworkImage(
|
||||
imageUrl: e.logoURL,
|
||||
imageUrl: e.logoURL!,
|
||||
width: 30,
|
||||
)
|
||||
: Icon(Icons.group, size: 30),
|
||||
@ -324,13 +324,13 @@ class _GroupSettingsScreenState extends SafeState<GroupSettingsScreen> {
|
||||
try {
|
||||
final name = await askUserString(
|
||||
context: context,
|
||||
title: tr("New conversation name"),
|
||||
message: tr("Please give a name to the new conversation"),
|
||||
title: tr("New conversation name")!,
|
||||
message: tr("Please give a name to the new conversation")!,
|
||||
defaultValue: "",
|
||||
hint: tr("Name"),
|
||||
hint: tr("Name")!,
|
||||
minLength: 1,
|
||||
maxLength: ServerConfigurationHelper
|
||||
.config.conversationsPolicy.maxConversationNameLen,
|
||||
.config!.conversationsPolicy.maxConversationNameLen,
|
||||
);
|
||||
|
||||
if (name == null) return;
|
||||
@ -345,27 +345,27 @@ class _GroupSettingsScreenState extends SafeState<GroupSettingsScreen> {
|
||||
if (visibility == null) return;
|
||||
|
||||
await GroupsHelper.createGroupConversation(NewGroupConversation(
|
||||
groupID: _groupSettings.id,
|
||||
groupID: _groupSettings!.id,
|
||||
name: name,
|
||||
minMembershipLevel: visibility,
|
||||
));
|
||||
|
||||
_key.currentState.refresh();
|
||||
_key.currentState!.refresh();
|
||||
} catch (e, s) {
|
||||
logError(e, s);
|
||||
snack(context, tr("Failed to create a conversation!"));
|
||||
snack(context, tr("Failed to create a conversation!")!);
|
||||
}
|
||||
}
|
||||
|
||||
void _changeConversationVisibility(
|
||||
Conversation conv, GroupMembershipLevel newLevel) async {
|
||||
Conversation conv, GroupMembershipLevel? newLevel) async {
|
||||
try {
|
||||
await GroupsHelper.setConversationVisibility(conv.id, newLevel);
|
||||
|
||||
_key.currentState.refresh();
|
||||
_key.currentState!.refresh();
|
||||
} catch (e, s) {
|
||||
logError(e, s);
|
||||
snack(context, tr("Failed to change conversation visibility level!"));
|
||||
snack(context, tr("Failed to change conversation visibility level!")!);
|
||||
}
|
||||
}
|
||||
|
||||
@ -378,21 +378,21 @@ class _GroupSettingsScreenState extends SafeState<GroupSettingsScreen> {
|
||||
|
||||
await GroupsHelper.deleteConversation(conv.id);
|
||||
|
||||
_key.currentState.refresh();
|
||||
_key.currentState!.refresh();
|
||||
} catch (e, s) {
|
||||
logError(e, s);
|
||||
snack(context, tr("Failed to delete conversation!"));
|
||||
snack(context, tr("Failed to delete conversation!")!);
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildGroupLogoArea() {
|
||||
SettingsSection _buildGroupLogoArea() {
|
||||
return SettingsSection(
|
||||
title: tr("Group logo"),
|
||||
tiles: [
|
||||
// Current logo
|
||||
SettingsTile(
|
||||
title: tr("Current logo"),
|
||||
leading: GroupIcon(group: _groupSettings),
|
||||
leading: GroupIcon(group: _groupSettings!),
|
||||
),
|
||||
|
||||
// Upload a new logo
|
||||
@ -421,10 +421,10 @@ class _GroupSettingsScreenState extends SafeState<GroupSettingsScreen> {
|
||||
try {
|
||||
final logo = await pickImage(context);
|
||||
if (logo == null) return;
|
||||
await _doUploadLogo(logo.bytes);
|
||||
await _doUploadLogo(logo.bytes as Uint8List?);
|
||||
} catch (e, stack) {
|
||||
print("Could not upload new logo! $e\n$stack");
|
||||
showSimpleSnack(context, tr("Could not upload new logo!"));
|
||||
showSimpleSnack(context, tr("Could not upload new logo!")!);
|
||||
}
|
||||
}
|
||||
|
||||
@ -435,13 +435,13 @@ class _GroupSettingsScreenState extends SafeState<GroupSettingsScreen> {
|
||||
await _doUploadLogo(newLogo);
|
||||
} catch (e, stack) {
|
||||
print("Could not generate new logo! $e\n$stack");
|
||||
showSimpleSnack(context, tr("Could not generate new random logo!"));
|
||||
showSimpleSnack(context, tr("Could not generate new random logo!")!);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _doUploadLogo(Uint8List bytes) async {
|
||||
await GroupsHelper.uploadNewLogo(_groupSettings.id, bytes);
|
||||
_key.currentState.refresh();
|
||||
Future<void> _doUploadLogo(Uint8List? bytes) async {
|
||||
await GroupsHelper.uploadNewLogo(_groupSettings!.id, bytes);
|
||||
_key.currentState!.refresh();
|
||||
}
|
||||
|
||||
/// Delete previous group logo
|
||||
@ -452,15 +452,15 @@ class _GroupSettingsScreenState extends SafeState<GroupSettingsScreen> {
|
||||
message: tr("Do you really want to delete the logo of this group ?")))
|
||||
return;
|
||||
|
||||
await GroupsHelper.deleteLogo(_groupSettings.id);
|
||||
_key.currentState.refresh();
|
||||
await GroupsHelper.deleteLogo(_groupSettings!.id);
|
||||
_key.currentState!.refresh();
|
||||
} catch (e, s) {
|
||||
print("Could not delete group logo! $e\n$s");
|
||||
showSimpleSnack(context, tr("Could not delete group logo!"));
|
||||
showSimpleSnack(context, tr("Could not delete group logo!")!);
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildDangerZone() {
|
||||
SettingsSection _buildDangerZone() {
|
||||
return SettingsSection(
|
||||
title: tr("Danger zone"),
|
||||
tiles: [
|
||||
@ -485,12 +485,12 @@ class _GroupSettingsScreenState extends SafeState<GroupSettingsScreen> {
|
||||
"Do you really want to delete this group ? All the posts related to it will be permanently deleted!")))
|
||||
return;
|
||||
|
||||
await GroupsHelper.deleteGroup(_groupSettings.id, password);
|
||||
await GroupsHelper.deleteGroup(_groupSettings!.id, password);
|
||||
|
||||
MainController.of(context).popPage();
|
||||
MainController.of(context)!.popPage();
|
||||
} catch (e, s) {
|
||||
print("Could not delete the group! $e\n$s");
|
||||
showSimpleSnack(context, tr("Could not delete the group"));
|
||||
showSimpleSnack(context, tr("Could not delete the group")!);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ class GroupsListScreen extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _GroupsListScreenState extends SafeState<GroupsListScreen> {
|
||||
GroupsList _groups;
|
||||
GroupsList? _groups;
|
||||
bool _error = false;
|
||||
|
||||
final _refreshIndicatorKey = GlobalKey<RefreshIndicatorState>();
|
||||
@ -41,7 +41,7 @@ class _GroupsListScreenState extends SafeState<GroupsListScreen> {
|
||||
hide: !_error,
|
||||
actions: [
|
||||
MaterialButton(
|
||||
child: Text(tr("Try again").toUpperCase()),
|
||||
child: Text(tr("Try again")!.toUpperCase()),
|
||||
onPressed: () => _refreshList(),
|
||||
),
|
||||
],
|
||||
@ -61,19 +61,19 @@ class _GroupsListScreenState extends SafeState<GroupsListScreen> {
|
||||
}
|
||||
|
||||
Widget _buildGroupsList() => ListView(
|
||||
children: (_groups.values.toList()
|
||||
children: (_groups!.values.toList()
|
||||
..sort((one, two) => two.id.compareTo(one.id)))
|
||||
.map((g) => ListTile(
|
||||
leading: GroupIcon(group: g),
|
||||
title: Text(g.displayName),
|
||||
subtitle: GroupMembershipWidget(
|
||||
group: g,
|
||||
onUpdated: () => _refreshIndicatorKey.currentState.show(),
|
||||
onUpdated: () => _refreshIndicatorKey.currentState!.show(),
|
||||
),
|
||||
trailing: IconButton(
|
||||
icon: Icon(Icons.delete),
|
||||
onPressed: () => _deleteGroup(g)),
|
||||
onTap: () => MainController.of(context).openGroup(g.id),
|
||||
onTap: () => MainController.of(context)!.openGroup(g.id),
|
||||
))
|
||||
.toList(),
|
||||
);
|
||||
@ -116,10 +116,10 @@ class _GroupsListScreenState extends SafeState<GroupsListScreen> {
|
||||
|
||||
if (!await GroupsHelper().removeMembership(g.id))
|
||||
showSimpleSnack(
|
||||
context, tr("Could not remove your membership to this group!"));
|
||||
context, tr("Could not remove your membership to this group!")!);
|
||||
|
||||
// Refresh the list of groups
|
||||
_refreshIndicatorKey.currentState.show();
|
||||
_refreshIndicatorKey.currentState!.show();
|
||||
}
|
||||
|
||||
/// Add a group
|
||||
@ -127,10 +127,10 @@ class _GroupsListScreenState extends SafeState<GroupsListScreen> {
|
||||
try {
|
||||
final name = await askUserString(
|
||||
context: context,
|
||||
title: tr("Group name"),
|
||||
message: tr("Name of the group to create"),
|
||||
title: tr("Group name")!,
|
||||
message: tr("Name of the group to create")!,
|
||||
defaultValue: "",
|
||||
hint: tr("Name of the group"),
|
||||
hint: tr("Name of the group")!,
|
||||
maxLength: 50,
|
||||
);
|
||||
|
||||
@ -138,10 +138,10 @@ class _GroupsListScreenState extends SafeState<GroupsListScreen> {
|
||||
|
||||
final groupID = await GroupsHelper.create(name);
|
||||
|
||||
MainController.of(context).openGroup(groupID);
|
||||
MainController.of(context)!.openGroup(groupID);
|
||||
} catch (e, s) {
|
||||
print("Could not create a new group! $e\n$s");
|
||||
showSimpleSnack(context, tr("Could not create a new group!"));
|
||||
showSimpleSnack(context, tr("Could not create a new group!")!);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ class NotificationsScreen extends StatefulWidget {
|
||||
final bool useSmallDeleteButton;
|
||||
|
||||
const NotificationsScreen({
|
||||
Key key,
|
||||
Key? key,
|
||||
this.useSmallDeleteButton = false,
|
||||
}) : assert(useSmallDeleteButton != null),
|
||||
super(key: key);
|
||||
@ -38,9 +38,9 @@ class NotificationsScreen extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _NotificationsScreenState extends SafeState<NotificationsScreen> {
|
||||
NotificationsList _list;
|
||||
UsersList _users;
|
||||
GroupsList _groups;
|
||||
NotificationsList? _list;
|
||||
late UsersList _users;
|
||||
late GroupsList _groups;
|
||||
_Status _status = _Status.LOADING;
|
||||
|
||||
final _refreshKey = GlobalKey<RefreshIndicatorState>();
|
||||
@ -64,12 +64,12 @@ class _NotificationsScreenState extends SafeState<NotificationsScreen> {
|
||||
});
|
||||
|
||||
setStatus(_Status.NONE);
|
||||
} on Exception catch (e) {
|
||||
} on Exception catch (e, s) {
|
||||
print("Exception while getting the list of notifications!");
|
||||
print(e);
|
||||
} on Error catch (e) {
|
||||
print("$e, $s");
|
||||
} on Error catch (e, s) {
|
||||
print("Error while getting the list of notifications!");
|
||||
print(e.stackTrace);
|
||||
print("$e, $s");
|
||||
}
|
||||
}
|
||||
|
||||
@ -77,7 +77,7 @@ class _NotificationsScreenState extends SafeState<NotificationsScreen> {
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
this.listen<NewNumberNotifsEvent>((d) => _refreshKey.currentState.show());
|
||||
this.listen<NewNumberNotifsEvent>((d) => _refreshKey.currentState!.show());
|
||||
}
|
||||
|
||||
@override
|
||||
@ -121,14 +121,14 @@ class _NotificationsScreenState extends SafeState<NotificationsScreen> {
|
||||
|
||||
/// Build body
|
||||
Widget _buildBody() {
|
||||
if (_status == _Status.ERROR || _list.length == 0)
|
||||
if (_status == _Status.ERROR || _list!.length == 0)
|
||||
return SingleChildScrollView(
|
||||
physics: AlwaysScrollableScrollPhysics(),
|
||||
child: _buildSingleChildCases(),
|
||||
);
|
||||
|
||||
return ListView(
|
||||
children: _list
|
||||
children: _list!
|
||||
.map((f) => _NotificationTile(
|
||||
notification: f,
|
||||
usersList: _users,
|
||||
@ -146,16 +146,16 @@ class _NotificationsScreenState extends SafeState<NotificationsScreen> {
|
||||
actions: [
|
||||
MaterialButton(
|
||||
onPressed: () => _loadList(),
|
||||
child: Text(tr("Try again".toUpperCase())),
|
||||
child: Text(tr("Try again".toUpperCase())!),
|
||||
)
|
||||
]);
|
||||
|
||||
// When there is no notification
|
||||
if (_list.length == 0)
|
||||
if (_list!.length == 0)
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Center(
|
||||
child: Text(tr("You do not have any notification now.")),
|
||||
child: Text(tr("You do not have any notification now.")!),
|
||||
),
|
||||
);
|
||||
|
||||
@ -165,7 +165,7 @@ class _NotificationsScreenState extends SafeState<NotificationsScreen> {
|
||||
/// Delete a notification
|
||||
void _deleteNotification(n.Notification notif) async {
|
||||
setState(() {
|
||||
_list.remove(notif);
|
||||
_list!.remove(notif);
|
||||
});
|
||||
|
||||
NotificationsHelper().markSeen(notif);
|
||||
@ -179,11 +179,11 @@ class _NotificationsScreenState extends SafeState<NotificationsScreen> {
|
||||
return;
|
||||
|
||||
if (!await NotificationsHelper().deleteAllNotifications()) {
|
||||
showSimpleSnack(context, tr("Could not delete all your notifications!"));
|
||||
showSimpleSnack(context, tr("Could not delete all your notifications!")!);
|
||||
return;
|
||||
}
|
||||
|
||||
_refreshKey.currentState.show();
|
||||
_refreshKey.currentState!.show();
|
||||
}
|
||||
}
|
||||
|
||||
@ -194,11 +194,11 @@ class _NotificationTile extends StatelessWidget {
|
||||
final void Function(n.Notification) onDelete;
|
||||
|
||||
const _NotificationTile({
|
||||
Key key,
|
||||
@required this.notification,
|
||||
@required this.usersList,
|
||||
@required this.groupsList,
|
||||
@required this.onDelete,
|
||||
Key? key,
|
||||
required this.notification,
|
||||
required this.usersList,
|
||||
required this.groupsList,
|
||||
required this.onDelete,
|
||||
}) : assert(notification != null),
|
||||
assert(usersList != null),
|
||||
assert(groupsList != null),
|
||||
@ -214,51 +214,51 @@ class _NotificationTile extends StatelessWidget {
|
||||
switch (notification.type) {
|
||||
// Comment
|
||||
case n.NotificationType.COMMENT_CREATED:
|
||||
message += tr("posted a comment");
|
||||
message += tr("posted a comment")!;
|
||||
break;
|
||||
|
||||
// Friendship requests
|
||||
case n.NotificationType.SENT_FRIEND_REQUEST:
|
||||
message += tr("sent you a friendship request.");
|
||||
message += tr("sent you a friendship request.")!;
|
||||
break;
|
||||
|
||||
case n.NotificationType.ACCEPTED_FRIEND_REQUEST:
|
||||
message += tr("accepted your friendship request.");
|
||||
message += tr("accepted your friendship request.")!;
|
||||
break;
|
||||
|
||||
case n.NotificationType.REJECTED_FRIEND_REQUEST:
|
||||
message += tr("rejected your friendship request.");
|
||||
message += tr("rejected your friendship request.")!;
|
||||
break;
|
||||
|
||||
// Groups membership
|
||||
case n.NotificationType.SENT_GROUP_MEMBERSHIP_INVITATION:
|
||||
message += tr("invited you to join the group");
|
||||
message += tr("invited you to join the group")!;
|
||||
break;
|
||||
|
||||
case n.NotificationType.ACCEPTED_GROUP_MEMBERSHIP_INVITATION:
|
||||
message += tr("accepted his invitation to join the group");
|
||||
message += tr("accepted his invitation to join the group")!;
|
||||
break;
|
||||
|
||||
case n.NotificationType.REJECTED_GROUP_MEMBERSHIP_INVITATION:
|
||||
message += tr("rejected his invitation to join the group");
|
||||
message += tr("rejected his invitation to join the group")!;
|
||||
break;
|
||||
|
||||
case n.NotificationType.SENT_GROUP_MEMBERSHIP_REQUEST:
|
||||
message += tr("sent a request to join the group");
|
||||
message += tr("sent a request to join the group")!;
|
||||
break;
|
||||
|
||||
case n.NotificationType.ACCEPTED_GROUP_MEMBERSHIP_REQUEST:
|
||||
message += tr("accepted you request to join the group");
|
||||
message += tr("accepted you request to join the group")!;
|
||||
break;
|
||||
|
||||
case n.NotificationType.REJECTED_GROUP_MEMBERSHIP_REQUEST:
|
||||
message += tr("rejected your request to join the group");
|
||||
message += tr("rejected your request to join the group")!;
|
||||
break;
|
||||
|
||||
// Generic element creation
|
||||
case n.NotificationType.ELEM_CREATED:
|
||||
if (notification.onElemType == n.NotificationElementType.POST)
|
||||
message += tr("created a new post");
|
||||
message += tr("created a new post")!;
|
||||
break;
|
||||
|
||||
case n.NotificationType.ELEM_UPDATED:
|
||||
@ -274,24 +274,24 @@ class _NotificationTile extends StatelessWidget {
|
||||
// User page
|
||||
if (notification.fromContainerType == n.NotificationElementType.USER_PAGE) {
|
||||
if (notification.fromUser == notification.fromContainerId)
|
||||
message += tr("on his / her page");
|
||||
message += tr("on his / her page")!;
|
||||
else
|
||||
message += tr("on %user_name%'s page", args: {
|
||||
"user_name": usersList.getUser(notification.fromContainerId).fullName
|
||||
});
|
||||
})!;
|
||||
}
|
||||
|
||||
// Group page
|
||||
if (notification.fromContainerType ==
|
||||
n.NotificationElementType.GROUP_PAGE) {
|
||||
message += tr("on the group %group%.", args: {
|
||||
"group": groupsList[notification.fromContainerId].displayName
|
||||
});
|
||||
"group": groupsList[notification.fromContainerId]!.displayName
|
||||
})!;
|
||||
}
|
||||
|
||||
// Group membership
|
||||
if (notification.onElemType == n.NotificationElementType.GROUP_MEMBERSHIP)
|
||||
message += groupsList[notification.onElemId].displayName;
|
||||
message += groupsList[notification.onElemId]!.displayName;
|
||||
|
||||
return CustomListTile(
|
||||
leading: AccountImageWidget(
|
||||
@ -299,11 +299,11 @@ class _NotificationTile extends StatelessWidget {
|
||||
),
|
||||
onTap: () => _onTap(context),
|
||||
title: Text(message),
|
||||
subtitle: Text(diffTimeFromNowToStr(notification.timeCreate)),
|
||||
subtitle: Text(diffTimeFromNowToStr(notification.timeCreate)!),
|
||||
onLongPressOpenMenu: (position) {
|
||||
showMenu(context: context, position: position, items: [
|
||||
PopupMenuItem(
|
||||
child: Text(tr("Delete")),
|
||||
child: Text(tr("Delete")!),
|
||||
value: _PopupMenuActions.DELETE,
|
||||
),
|
||||
]).then(_popupMenuAction);
|
||||
@ -311,7 +311,7 @@ class _NotificationTile extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
void _popupMenuAction(_PopupMenuActions value) {
|
||||
void _popupMenuAction(_PopupMenuActions? value) {
|
||||
switch (value) {
|
||||
case _PopupMenuActions.DELETE:
|
||||
onDelete(notification);
|
||||
@ -327,10 +327,10 @@ class _NotificationTile extends StatelessWidget {
|
||||
openUserPage(userID: notification.fromUser, context: context);
|
||||
} else if (notification.onElemType ==
|
||||
n.NotificationElementType.GROUP_MEMBERSHIP) {
|
||||
MainController.of(context).openGroup(notification.onElemId);
|
||||
MainController.of(context)!.openGroup(notification.onElemId);
|
||||
} else {
|
||||
showSimpleSnack(context,
|
||||
tr("This kind of notification is not supported yet by this application."));
|
||||
tr("This kind of notification is not supported yet by this application.")!);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -16,8 +16,8 @@ class OtherUserFriendsListScreen extends StatefulWidget {
|
||||
final bool enableAppBar;
|
||||
|
||||
const OtherUserFriendsListScreen({
|
||||
Key key,
|
||||
@required this.userID,
|
||||
Key? key,
|
||||
required this.userID,
|
||||
this.enableAppBar = true,
|
||||
}) : assert(userID != null),
|
||||
assert(enableAppBar != null),
|
||||
@ -33,12 +33,12 @@ class _OtherUserFriendsListScreenState
|
||||
final FriendsHelper friendsHelper = FriendsHelper();
|
||||
final UsersHelper usersHelper = UsersHelper();
|
||||
|
||||
Set<int> _friendsList;
|
||||
UsersList _usersInfo;
|
||||
late Set<int> _friendsList;
|
||||
UsersList? _usersInfo;
|
||||
bool _error = false;
|
||||
|
||||
String get _routeName => tr("Friends of %name%",
|
||||
args: {"name": _usersInfo.getUser(widget.userID).displayName});
|
||||
String? get _routeName => tr("Friends of %name%",
|
||||
args: {"name": _usersInfo!.getUser(widget.userID).displayName});
|
||||
|
||||
void setError(bool e) => setState(() => _error = e);
|
||||
|
||||
@ -79,16 +79,16 @@ class _OtherUserFriendsListScreenState
|
||||
return Scaffold(
|
||||
appBar: widget.enableAppBar
|
||||
? AppBar(
|
||||
title: Text(_routeName),
|
||||
title: Text(_routeName!),
|
||||
)
|
||||
: null,
|
||||
body: ListView.builder(
|
||||
itemCount: _friendsList.length,
|
||||
itemBuilder: (c, i) => SimpleUserTile(
|
||||
user: _usersInfo.getUser(_friendsList.elementAt(i)),
|
||||
user: _usersInfo!.getUser(_friendsList.elementAt(i)),
|
||||
onTap: (u) => openUserPage(
|
||||
context: context,
|
||||
userID: u.id,
|
||||
userID: u.id!,
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -104,7 +104,7 @@ class _OtherUserFriendsListScreenState
|
||||
actions: [
|
||||
TextButton(
|
||||
child: Text(
|
||||
tr("Try again").toUpperCase(),
|
||||
tr("Try again")!.toUpperCase(),
|
||||
style: TextStyle(color: Colors.white),
|
||||
),
|
||||
onPressed: load,
|
||||
|
@ -24,9 +24,9 @@ class SearchScreen extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _SearchScreenState extends State<SearchScreen> {
|
||||
SearchResultsList _searchResultsList;
|
||||
UsersList _usersList;
|
||||
GroupsList _groupsList;
|
||||
SearchResultsList? _searchResultsList;
|
||||
late UsersList _usersList;
|
||||
late GroupsList _groupsList;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -44,13 +44,13 @@ class _SearchScreenState extends State<SearchScreen> {
|
||||
? Container()
|
||||
: Expanded(
|
||||
child: ListView(
|
||||
children: _searchResultsList
|
||||
children: _searchResultsList!
|
||||
.map((f) => f.kind == SearchResultKind.USER
|
||||
? _SearchResultUser(
|
||||
user: _usersList.getUser(f.id),
|
||||
)
|
||||
: _SearchResultGroup(
|
||||
group: _groupsList[f.id],
|
||||
group: _groupsList[f.id]!,
|
||||
))
|
||||
.toList(),
|
||||
),
|
||||
@ -78,7 +78,7 @@ class _SearchScreenState extends State<SearchScreen> {
|
||||
print(e);
|
||||
print(stack);
|
||||
|
||||
showSimpleSnack(context, tr("Could not peform search!"));
|
||||
showSimpleSnack(context, tr("Could not peform search!")!);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -86,7 +86,7 @@ class _SearchScreenState extends State<SearchScreen> {
|
||||
class _SearchResultUser extends StatelessWidget {
|
||||
final User user;
|
||||
|
||||
const _SearchResultUser({Key key, this.user})
|
||||
const _SearchResultUser({Key? key, required this.user})
|
||||
: assert(user != null),
|
||||
super(key: key);
|
||||
|
||||
@ -97,7 +97,7 @@ class _SearchResultUser extends StatelessWidget {
|
||||
user: user,
|
||||
),
|
||||
title: Text(user.displayName),
|
||||
onTap: () => MainController.of(context).openUserPage(user.id),
|
||||
onTap: () => MainController.of(context)!.openUserPage(user.id!),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -105,7 +105,7 @@ class _SearchResultUser extends StatelessWidget {
|
||||
class _SearchResultGroup extends StatelessWidget {
|
||||
final Group group;
|
||||
|
||||
const _SearchResultGroup({Key key, this.group})
|
||||
const _SearchResultGroup({Key? key, required this.group})
|
||||
: assert(group != null),
|
||||
super(key: key);
|
||||
|
||||
@ -114,8 +114,8 @@ class _SearchResultGroup extends StatelessWidget {
|
||||
return ListTile(
|
||||
leading: GroupIcon(group: group),
|
||||
title: Text(group.displayName),
|
||||
subtitle: Text(tr("Group")),
|
||||
onTap: () => MainController.of(context).openGroup(group.id),
|
||||
subtitle: Text(tr("Group")!),
|
||||
onTap: () => MainController.of(context)!.openGroup(group.id),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import 'package:comunic/helpers/users_helper.dart';
|
||||
import 'package:comunic/lists/groups_list.dart';
|
||||
import 'package:comunic/lists/unread_conversations_list.dart';
|
||||
import 'package:comunic/lists/users_list.dart';
|
||||
import 'package:comunic/models/unread_conversation.dart';
|
||||
import 'package:comunic/ui/routes/main_route/main_route.dart';
|
||||
import 'package:comunic/ui/widgets/async_screen_widget.dart';
|
||||
import 'package:comunic/ui/widgets/conversation_image_widget.dart';
|
||||
@ -25,9 +26,9 @@ class UnreadConversationsScreen extends StatefulWidget {
|
||||
|
||||
class _UnreadConversationsScreenState
|
||||
extends SafeState<UnreadConversationsScreen> {
|
||||
UnreadConversationsList _list;
|
||||
UsersList _users;
|
||||
GroupsList _groups;
|
||||
late UnreadConversationsList _list;
|
||||
UsersList? _users;
|
||||
GroupsList? _groups;
|
||||
|
||||
final _key = GlobalKey<AsyncScreenWidgetState>();
|
||||
|
||||
@ -41,7 +42,7 @@ class _UnreadConversationsScreenState
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
listen<NewNumberUnreadConversations>((e) => _key.currentState.refresh());
|
||||
listen<NewNumberUnreadConversations>((e) => _key.currentState!.refresh());
|
||||
}
|
||||
|
||||
@override
|
||||
@ -50,7 +51,7 @@ class _UnreadConversationsScreenState
|
||||
key: _key,
|
||||
onReload: _refresh,
|
||||
onBuild: _buildList,
|
||||
errorMessage: tr("Could not load the list of unread conversations!"),
|
||||
errorMessage: tr("Could not load the list of unread conversations!")!,
|
||||
);
|
||||
}
|
||||
|
||||
@ -61,7 +62,7 @@ class _UnreadConversationsScreenState
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(
|
||||
tr("You do not have any unread conversation yet..."),
|
||||
tr("You do not have any unread conversation yet...")!,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
@ -74,34 +75,34 @@ class _UnreadConversationsScreenState
|
||||
}
|
||||
|
||||
Widget _tileBuilder(BuildContext context, int index) {
|
||||
final conv = _list[index];
|
||||
final UnreadConversation conv = _list[index];
|
||||
|
||||
final message = _list[index].message;
|
||||
|
||||
final singleUserConv = conv.conv.members.length < 3;
|
||||
final singleUserConv = conv.conv.members!.length < 3;
|
||||
|
||||
String messageStr;
|
||||
String? messageStr;
|
||||
if (message.hasFile)
|
||||
messageStr = tr("New file");
|
||||
else if (message.hasMessage)
|
||||
messageStr = singleUserConv
|
||||
? message.message.content
|
||||
: tr("%1% : %2%", args: {
|
||||
"1": _users.getUser(message.userID).fullName,
|
||||
"1": _users!.getUser(message.userID).fullName,
|
||||
"2": message.message.content,
|
||||
});
|
||||
else
|
||||
message.serverMessage.getText(_users);
|
||||
message.serverMessage!.getText(_users);
|
||||
|
||||
return ListTile(
|
||||
leading: ConversationImageWidget(
|
||||
conversation: conv.conv,
|
||||
users: _users,
|
||||
users: _users!,
|
||||
group: conv.conv.isGroupConversation
|
||||
? _groups.getGroup(conv.conv.groupID)
|
||||
? _groups!.getGroup(conv.conv.groupID)
|
||||
: null,
|
||||
),
|
||||
title: Text(ConversationsHelper.getConversationName(conv.conv, _users)),
|
||||
title: Text(ConversationsHelper.getConversationName(conv.conv, _users)!),
|
||||
subtitle: RichText(
|
||||
text: TextSpan(style: Theme.of(context).textTheme.bodyText2, children: [
|
||||
TextSpan(
|
||||
@ -110,9 +111,9 @@ class _UnreadConversationsScreenState
|
||||
),
|
||||
]),
|
||||
),
|
||||
trailing: Text(diffTimeFromNowToStr(conv.message.timeSent)),
|
||||
trailing: Text(diffTimeFromNowToStr(conv.message.timeSent!)!),
|
||||
onTap: () =>
|
||||
MainController.of(context).openConversationById(conv.conv.id),
|
||||
MainController.of(context)!.openConversationById(conv.conv.id!),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ class UpdateConversationScreen extends StatefulWidget {
|
||||
final convID;
|
||||
|
||||
const UpdateConversationScreen({
|
||||
Key key,
|
||||
Key? key,
|
||||
this.convID,
|
||||
}) : super(key: key);
|
||||
|
||||
@ -41,19 +41,19 @@ class UpdateConversationScreen extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _UpdateConversationScreen extends State<UpdateConversationScreen> {
|
||||
Conversation _conversation;
|
||||
late Conversation _conversation;
|
||||
|
||||
TextEditingController _nameController = TextEditingController();
|
||||
TextEditingController _colorController = TextEditingController();
|
||||
UsersList _members = UsersList();
|
||||
Set<int> _admins = Set();
|
||||
Set<int?> _admins = Set();
|
||||
bool _followConversation = true;
|
||||
bool _canEveryoneAddMembers = true;
|
||||
String _image;
|
||||
bool? _canEveryoneAddMembers = true;
|
||||
String? _image;
|
||||
|
||||
String get _conversationColor => _colorController.text;
|
||||
|
||||
Color get _color {
|
||||
Color? get _color {
|
||||
if (_conversationColor == null || _conversationColor.isEmpty) return null;
|
||||
|
||||
try {
|
||||
@ -72,7 +72,7 @@ class _UpdateConversationScreen extends State<UpdateConversationScreen> {
|
||||
isUpdating && _conversation.isGroupConversation;
|
||||
|
||||
bool get _canAddMembers =>
|
||||
(isAdmin || _conversation.canEveryoneAddMembers) &&
|
||||
(isAdmin || _conversation.canEveryoneAddMembers!) &&
|
||||
(!isUpdating || !_conversation.isManaged);
|
||||
|
||||
get _isValid => _members.length > 0;
|
||||
@ -104,8 +104,8 @@ class _UpdateConversationScreen extends State<UpdateConversationScreen> {
|
||||
appBar: AppBar(
|
||||
leading: ComunicBackButton(),
|
||||
title: Text(isUpdating
|
||||
? tr("Update conversation")
|
||||
: tr("Create a conversation")),
|
||||
? tr("Update conversation")!
|
||||
: tr("Create a conversation")!),
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: Icon(Icons.check),
|
||||
@ -115,7 +115,7 @@ class _UpdateConversationScreen extends State<UpdateConversationScreen> {
|
||||
body: AsyncScreenWidget(
|
||||
onReload: _init,
|
||||
onBuild: _buildBody,
|
||||
errorMessage: tr("Failed to load conversation settings!"),
|
||||
errorMessage: tr("Failed to load conversation settings!")!,
|
||||
),
|
||||
);
|
||||
|
||||
@ -126,7 +126,7 @@ class _UpdateConversationScreen extends State<UpdateConversationScreen> {
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
_isGroupConversation
|
||||
? Text(tr("This conversation is managed by a group"))
|
||||
? Text(tr("This conversation is managed by a group")!)
|
||||
: Container(),
|
||||
|
||||
// Conversation name
|
||||
@ -163,7 +163,7 @@ class _UpdateConversationScreen extends State<UpdateConversationScreen> {
|
||||
_followConversation = b;
|
||||
}),
|
||||
),
|
||||
Text(tr("Follow conversation"))
|
||||
Text(tr("Follow conversation")!)
|
||||
],
|
||||
),
|
||||
|
||||
@ -173,7 +173,7 @@ class _UpdateConversationScreen extends State<UpdateConversationScreen> {
|
||||
: Row(
|
||||
children: <Widget>[
|
||||
Switch.adaptive(
|
||||
value: _canEveryoneAddMembers,
|
||||
value: _canEveryoneAddMembers!,
|
||||
onChanged: isAdmin
|
||||
? (b) => setState(() {
|
||||
_canEveryoneAddMembers = b;
|
||||
@ -182,7 +182,7 @@ class _UpdateConversationScreen extends State<UpdateConversationScreen> {
|
||||
),
|
||||
Flexible(
|
||||
child: Text(tr(
|
||||
"Allow all members of the conversation to add users")))
|
||||
"Allow all members of the conversation to add users")!))
|
||||
],
|
||||
),
|
||||
|
||||
@ -190,7 +190,7 @@ class _UpdateConversationScreen extends State<UpdateConversationScreen> {
|
||||
PickUserWidget(
|
||||
resetOnChoose: true,
|
||||
keepFocusOnChoose: true,
|
||||
label: tr("Add member"),
|
||||
label: tr("Add member")!,
|
||||
enabled: _canAddMembers,
|
||||
onSelectUser: (user) => _addMember(user)),
|
||||
|
||||
@ -215,12 +215,12 @@ class _UpdateConversationScreen extends State<UpdateConversationScreen> {
|
||||
onSelected: (choice) => _membersMenuItemSelected(user, choice),
|
||||
itemBuilder: (c) => <PopupMenuEntry<_MembersMenuChoices>>[
|
||||
PopupMenuItem(
|
||||
child: Text(tr("Toggle admin status")),
|
||||
child: Text(tr("Toggle admin status")!),
|
||||
value: _MembersMenuChoices.TOGGLE_ADMIN_STATUS,
|
||||
enabled: isUpdating && isAdmin && user.id != userID(),
|
||||
),
|
||||
PopupMenuItem(
|
||||
child: Text(tr("Remove")),
|
||||
child: Text(tr("Remove")!),
|
||||
value: _MembersMenuChoices.REMOVE,
|
||||
enabled: isAdmin && user.id != userID(),
|
||||
),
|
||||
@ -260,7 +260,7 @@ class _UpdateConversationScreen extends State<UpdateConversationScreen> {
|
||||
setState(() => _members.insert(0, user));
|
||||
} catch (e, s) {
|
||||
logError(e, s);
|
||||
snack(context, tr("Failed to add member to conversation!"));
|
||||
snack(context, tr("Failed to add member to conversation!")!);
|
||||
}
|
||||
}
|
||||
|
||||
@ -275,14 +275,14 @@ class _UpdateConversationScreen extends State<UpdateConversationScreen> {
|
||||
});
|
||||
} catch (e, s) {
|
||||
logError(e, s);
|
||||
snack(context, tr("Failed to remove member!"));
|
||||
snack(context, tr("Failed to remove member!")!);
|
||||
}
|
||||
}
|
||||
|
||||
void _toggleAdminStatus(User user) async {
|
||||
try {
|
||||
final setAdmin = !_admins.contains(user.id);
|
||||
await ConversationsHelper.setAdmin(_conversation.id, user.id, setAdmin);
|
||||
await ConversationsHelper.setAdmin(_conversation.id!, user.id!, setAdmin);
|
||||
|
||||
setState(() {
|
||||
if (!setAdmin)
|
||||
@ -292,7 +292,7 @@ class _UpdateConversationScreen extends State<UpdateConversationScreen> {
|
||||
});
|
||||
} catch (e, s) {
|
||||
logError(e, s);
|
||||
snack(context, tr("Failed to toggle admin status of user!"));
|
||||
snack(context, tr("Failed to toggle admin status of user!")!);
|
||||
}
|
||||
}
|
||||
|
||||
@ -306,17 +306,17 @@ class _UpdateConversationScreen extends State<UpdateConversationScreen> {
|
||||
name: _nameController.text,
|
||||
members: _members.map((element) => element.id).toList(),
|
||||
follow: _followConversation,
|
||||
canEveryoneAddMembers: _canEveryoneAddMembers,
|
||||
color: _color));
|
||||
canEveryoneAddMembers: _canEveryoneAddMembers!,
|
||||
color: _color)) ;
|
||||
|
||||
MainController.of(context).popPage();
|
||||
MainController.of(context).openConversationById(conversationID);
|
||||
MainController.of(context)!.popPage();
|
||||
MainController.of(context)!.openConversationById(conversationID);
|
||||
return;
|
||||
}
|
||||
|
||||
// Update conversation settings
|
||||
final newSettings = NewConversationsSettings(
|
||||
convID: _conversation.id,
|
||||
convID: _conversation.id!,
|
||||
following: _followConversation,
|
||||
isComplete: isAdmin,
|
||||
name: _nameController.text,
|
||||
@ -326,10 +326,10 @@ class _UpdateConversationScreen extends State<UpdateConversationScreen> {
|
||||
|
||||
await ConversationsHelper.updateConversation(newSettings);
|
||||
|
||||
MainController.of(context).popPage();
|
||||
MainController.of(context)!.popPage();
|
||||
} catch (e, s) {
|
||||
logError(e, s);
|
||||
snack(context, tr("Failed to update conversation settings!"));
|
||||
snack(context, tr("Failed to update conversation settings!")!);
|
||||
}
|
||||
}
|
||||
|
||||
@ -337,12 +337,12 @@ class _UpdateConversationScreen extends State<UpdateConversationScreen> {
|
||||
Widget _buildConversationImageWidget() => Column(
|
||||
children: [
|
||||
SizedBox(height: 10),
|
||||
Text(tr("Conversation logo"),
|
||||
Text(tr("Conversation logo")!,
|
||||
style: TextStyle(fontWeight: FontWeight.bold)),
|
||||
SizedBox(height: 5),
|
||||
_image == null
|
||||
? Text("No logo defined yet.")
|
||||
: CachedNetworkImage(imageUrl: _image),
|
||||
: CachedNetworkImage(imageUrl: _image!),
|
||||
SizedBox(height: 5),
|
||||
isAdmin
|
||||
? Row(
|
||||
@ -350,14 +350,14 @@ class _UpdateConversationScreen extends State<UpdateConversationScreen> {
|
||||
children: [
|
||||
OutlinedButton(
|
||||
onPressed: _uploadNewLogo,
|
||||
child: Text(tr("Change logo")),
|
||||
child: Text(tr("Change logo")!),
|
||||
),
|
||||
SizedBox(width: 5),
|
||||
_image == null
|
||||
? Container()
|
||||
: ElevatedButton(
|
||||
onPressed: _deleteLogo,
|
||||
child: Text(tr("Delete logo")),
|
||||
child: Text(tr("Delete logo")!),
|
||||
style: ButtonStyle(
|
||||
backgroundColor:
|
||||
MaterialStateProperty.all(Colors.red)),
|
||||
@ -375,8 +375,8 @@ class _UpdateConversationScreen extends State<UpdateConversationScreen> {
|
||||
final newLogo = await showPickFileDialog(
|
||||
context: context,
|
||||
allowedMimeTypes: ["image/png", "image/jpeg", "image/gif"],
|
||||
imageMaxWidth: srvConfig.conversationsPolicy.maxLogoWidth,
|
||||
imageMaxHeight: srvConfig.conversationsPolicy.maxLogoHeight,
|
||||
imageMaxWidth: srvConfig!.conversationsPolicy.maxLogoWidth,
|
||||
imageMaxHeight: srvConfig!.conversationsPolicy.maxLogoHeight,
|
||||
);
|
||||
|
||||
if (newLogo == null) return;
|
||||
@ -388,7 +388,7 @@ class _UpdateConversationScreen extends State<UpdateConversationScreen> {
|
||||
setState(() => _image = newConvSettings.logoURL);
|
||||
} catch (e, s) {
|
||||
logError(e, s);
|
||||
snack(context, tr("Failed to change conversation logo !"));
|
||||
snack(context, tr("Failed to change conversation logo !")!);
|
||||
}
|
||||
}
|
||||
|
||||
@ -404,7 +404,7 @@ class _UpdateConversationScreen extends State<UpdateConversationScreen> {
|
||||
setState(() => _image = null);
|
||||
} catch (e, s) {
|
||||
logError(e, s);
|
||||
snack(context, tr("Failed to remove conversation logo!"));
|
||||
snack(context, tr("Failed to remove conversation logo!")!);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ import 'package:flutter/material.dart';
|
||||
class UserAccessDeniedScreen extends StatefulWidget {
|
||||
final int userID;
|
||||
|
||||
const UserAccessDeniedScreen({Key key, @required this.userID})
|
||||
const UserAccessDeniedScreen({Key? key, required this.userID})
|
||||
: assert(userID != null),
|
||||
super(key: key);
|
||||
|
||||
@ -31,8 +31,8 @@ class _UserAccessDeniedScreenState extends SafeState<UserAccessDeniedScreen> {
|
||||
|
||||
final _key = GlobalKey<AsyncScreenWidgetState>();
|
||||
|
||||
FriendStatus _status;
|
||||
User _user;
|
||||
late FriendStatus _status;
|
||||
late User _user;
|
||||
|
||||
Future<void> refresh() async {
|
||||
final status = await friendsHelper.getFriendshipStatus(widget.userID);
|
||||
@ -40,7 +40,7 @@ class _UserAccessDeniedScreenState extends SafeState<UserAccessDeniedScreen> {
|
||||
|
||||
// Check if the two users are friend now
|
||||
if (status.areFriend) {
|
||||
final controller = MainController.of(context);
|
||||
final controller = MainController.of(context)!;
|
||||
controller.popPage();
|
||||
controller.openUserPage(widget.userID);
|
||||
}
|
||||
@ -55,7 +55,7 @@ class _UserAccessDeniedScreenState extends SafeState<UserAccessDeniedScreen> {
|
||||
key: _key,
|
||||
onReload: refresh,
|
||||
onBuild: _buildPage,
|
||||
errorMessage: tr("Could not load friendship information!"));
|
||||
errorMessage: tr("Could not load friendship information!")!);
|
||||
}
|
||||
|
||||
Widget _buildPage() {
|
||||
@ -77,10 +77,10 @@ class _UserAccessDeniedScreenState extends SafeState<UserAccessDeniedScreen> {
|
||||
_user.displayName,
|
||||
style: TextStyle(fontSize: 25.0),
|
||||
),
|
||||
Text(tr("This account is private.")),
|
||||
Text(tr("This account is private.")!),
|
||||
FriendshipStatusWidget(
|
||||
status: _status,
|
||||
onFriendshipUpdated: () => _key.currentState.refresh(),
|
||||
onFriendshipUpdated: () => _key.currentState!.refresh(),
|
||||
)
|
||||
],
|
||||
),
|
||||
|
@ -20,7 +20,7 @@ enum _PageStatus { LOADING, ERROR, READY }
|
||||
class UserPageScreen extends StatefulWidget {
|
||||
final int userID;
|
||||
|
||||
const UserPageScreen({Key key, @required this.userID})
|
||||
const UserPageScreen({Key? key, required this.userID})
|
||||
: assert(userID != null),
|
||||
super(key: key);
|
||||
|
||||
@ -34,8 +34,8 @@ class _UserPageScreenState extends SafeState<UserPageScreen> {
|
||||
|
||||
// Objects members
|
||||
_PageStatus _status = _PageStatus.LOADING;
|
||||
AdvancedUserInfo _userInfo;
|
||||
FriendStatus _frienshipStatus;
|
||||
late AdvancedUserInfo _userInfo;
|
||||
FriendStatus? _frienshipStatus;
|
||||
final _refreshIndicatorKey = GlobalKey<RefreshIndicatorState>();
|
||||
|
||||
_setStatus(_PageStatus s) => setState(() => _status = s);
|
||||
@ -65,7 +65,7 @@ class _UserPageScreenState extends SafeState<UserPageScreen> {
|
||||
_setStatus(_PageStatus.ERROR);
|
||||
|
||||
if (e.cause == GetUserAdvancedInformationErrorCause.NOT_AUTHORIZED) {
|
||||
final controller = MainController.of(context);
|
||||
final controller = MainController.of(context)!;
|
||||
controller.popPage();
|
||||
controller.openUserAccessDeniedPage(widget.userID);
|
||||
}
|
||||
@ -94,7 +94,7 @@ class _UserPageScreenState extends SafeState<UserPageScreen> {
|
||||
Widget _buildError() {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(tr("Error")),
|
||||
title: Text(tr("Error")!),
|
||||
),
|
||||
body: Center(
|
||||
child:
|
||||
@ -102,7 +102,7 @@ class _UserPageScreenState extends SafeState<UserPageScreen> {
|
||||
TextButton(
|
||||
onPressed: _getUserInfo,
|
||||
child: Text(
|
||||
tr("Retry").toUpperCase(),
|
||||
tr("Retry")!.toUpperCase(),
|
||||
style: TextStyle(color: Colors.white),
|
||||
),
|
||||
)
|
||||
@ -114,12 +114,12 @@ class _UserPageScreenState extends SafeState<UserPageScreen> {
|
||||
return isTablet(context)
|
||||
? UserPageTablet(
|
||||
userInfo: _userInfo,
|
||||
onNeedRefresh: () => _refreshIndicatorKey.currentState.show(),
|
||||
onNeedRefresh: () => _refreshIndicatorKey.currentState!.show(),
|
||||
friendshipStatus: _frienshipStatus,
|
||||
)
|
||||
: UserMobilePage(
|
||||
userInfo: _userInfo,
|
||||
onNeedRefresh: () => _refreshIndicatorKey.currentState.show(),
|
||||
onNeedRefresh: () => _refreshIndicatorKey.currentState!.show(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -19,8 +19,8 @@ class AboutUserSection extends StatefulWidget {
|
||||
final AdvancedUserInfo user;
|
||||
|
||||
const AboutUserSection({
|
||||
Key key,
|
||||
@required this.user,
|
||||
Key? key,
|
||||
required this.user,
|
||||
}) : assert(user != null),
|
||||
super(key: key);
|
||||
|
||||
@ -29,7 +29,7 @@ class AboutUserSection extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _AboutUserSectionState extends State<AboutUserSection> {
|
||||
FriendStatus _friendStatus;
|
||||
late FriendStatus _friendStatus;
|
||||
|
||||
final _screenKey = GlobalKey<AsyncScreenWidgetState>();
|
||||
|
||||
@ -38,14 +38,14 @@ class _AboutUserSectionState extends State<AboutUserSection> {
|
||||
_friendStatus = await FriendsHelper().getFriendshipStatus(widget.user.id);
|
||||
}
|
||||
|
||||
void _toggleRefresh() => _screenKey.currentState.refresh();
|
||||
void _toggleRefresh() => _screenKey.currentState!.refresh();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => AsyncScreenWidget(
|
||||
key: _screenKey,
|
||||
onReload: _init,
|
||||
onBuild: _buildList,
|
||||
errorMessage: tr("Failed to load user information!"));
|
||||
errorMessage: tr("Failed to load user information!")!);
|
||||
|
||||
Widget _buildList() => ListView(
|
||||
children: [
|
||||
@ -58,7 +58,7 @@ class _AboutUserSectionState extends State<AboutUserSection> {
|
||||
widget.user.hasPersonalWebsite
|
||||
? ListTile(
|
||||
leading: Icon(Icons.link),
|
||||
title: Text(tr("Personal Website")),
|
||||
title: Text(tr("Personal Website")!),
|
||||
subtitle: Text(widget.user.personalWebsite),
|
||||
onTap: () => launch(widget.user.personalWebsite),
|
||||
)
|
||||
@ -68,7 +68,7 @@ class _AboutUserSectionState extends State<AboutUserSection> {
|
||||
widget.user.hasPublicNote
|
||||
? ListTile(
|
||||
leading: Icon(Icons.note),
|
||||
title: Text(tr("Note")),
|
||||
title: Text(tr("Note")!),
|
||||
subtitle: TextWidget(
|
||||
content: DisplayedString(widget.user.publicNote)),
|
||||
)
|
||||
@ -78,7 +78,7 @@ class _AboutUserSectionState extends State<AboutUserSection> {
|
||||
widget.user.hasVirtualDirectory
|
||||
? ListTile(
|
||||
leading: Icon(Icons.alternate_email),
|
||||
title: Text(tr("Virtual directory")),
|
||||
title: Text(tr("Virtual directory")!),
|
||||
subtitle: Text("@${widget.user.virtualDirectory}"))
|
||||
: Container(),
|
||||
|
||||
@ -87,10 +87,10 @@ class _AboutUserSectionState extends State<AboutUserSection> {
|
||||
? Container()
|
||||
: ListTile(
|
||||
leading: Icon(Icons.email_outlined),
|
||||
title: Text(tr("Email address")),
|
||||
subtitle: Text(widget.user.emailAddress),
|
||||
title: Text(tr("Email address")!),
|
||||
subtitle: Text(widget.user.emailAddress!),
|
||||
onTap: () =>
|
||||
copyToClipboard(context, widget.user.emailAddress),
|
||||
copyToClipboard(context, widget.user.emailAddress!),
|
||||
),
|
||||
|
||||
// User location
|
||||
@ -98,16 +98,16 @@ class _AboutUserSectionState extends State<AboutUserSection> {
|
||||
? Container()
|
||||
: ListTile(
|
||||
leading: Icon(Icons.location_on),
|
||||
title: Text(tr("Location")),
|
||||
subtitle: Text(widget.user.location),
|
||||
onTap: () => copyToClipboard(context, widget.user.location),
|
||||
title: Text(tr("Location")!),
|
||||
subtitle: Text(widget.user.location!),
|
||||
onTap: () => copyToClipboard(context, widget.user.location!),
|
||||
),
|
||||
|
||||
// Number of friends
|
||||
widget.user.isFriendsListPublic
|
||||
? ListTile(
|
||||
leading: Icon(Icons.group),
|
||||
title: Text(tr("Number of friends")),
|
||||
title: Text(tr("Number of friends")!),
|
||||
subtitle: Text(widget.user.numberFriends.toString()),
|
||||
)
|
||||
: Container(),
|
||||
@ -115,20 +115,20 @@ class _AboutUserSectionState extends State<AboutUserSection> {
|
||||
// Member for
|
||||
ListTile(
|
||||
leading: Icon(Icons.access_time_rounded),
|
||||
title: Text(tr("Member for")),
|
||||
title: Text(tr("Member for")!),
|
||||
subtitle:
|
||||
Text(diffTimeFromNowToStr(widget.user.accountCreationTime)),
|
||||
Text(diffTimeFromNowToStr(widget.user.accountCreationTime)!),
|
||||
),
|
||||
|
||||
// Account visibility
|
||||
ListTile(
|
||||
leading: Icon(Icons.remove_red_eye),
|
||||
title: Text(tr("Account visibility")),
|
||||
title: Text(tr("Account visibility")!),
|
||||
subtitle: Text(widget.user.pageVisibility == UserPageVisibility.OPEN
|
||||
? tr("Open page")
|
||||
? tr("Open page")!
|
||||
: (widget.user.pageVisibility == UserPageVisibility.PUBLIC
|
||||
? tr("Public page")
|
||||
: tr("Private page"))),
|
||||
? tr("Public page")!
|
||||
: tr("Private page")!)),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
@ -14,9 +14,9 @@ class UserPageHeader extends StatelessWidget {
|
||||
final Color bgColor;
|
||||
|
||||
const UserPageHeader({
|
||||
Key key,
|
||||
@required this.user,
|
||||
@required this.bgColor,
|
||||
Key? key,
|
||||
required this.user,
|
||||
required this.bgColor,
|
||||
}) : assert(user != null),
|
||||
super(key: key);
|
||||
|
||||
|
@ -13,8 +13,8 @@ class UserPostsSection extends StatefulWidget {
|
||||
final AdvancedUserInfo user;
|
||||
|
||||
const UserPostsSection({
|
||||
Key key,
|
||||
@required this.user,
|
||||
Key? key,
|
||||
required this.user,
|
||||
}) : assert(user != null),
|
||||
super(key: key);
|
||||
|
||||
@ -23,7 +23,7 @@ class UserPostsSection extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _UserPostsSectionState extends State<UserPostsSection> {
|
||||
int get _userID => widget.user.id;
|
||||
int? get _userID => widget.user.id;
|
||||
|
||||
final _postsKey = GlobalKey<PostsListWidgetState>();
|
||||
|
||||
@ -34,7 +34,7 @@ class _UserPostsSectionState extends State<UserPostsSection> {
|
||||
widget.user.canPostTexts
|
||||
? PostCreateFormWidget(
|
||||
postTarget: PostTarget.USER_PAGE,
|
||||
targetID: _userID,
|
||||
targetID: _userID!,
|
||||
onCreated: _postCreated,
|
||||
)
|
||||
: Container()
|
||||
@ -45,6 +45,6 @@ class _UserPostsSectionState extends State<UserPostsSection> {
|
||||
);
|
||||
|
||||
void _postCreated() {
|
||||
_postsKey.currentState.loadPostsList(getOlder: false);
|
||||
_postsKey.currentState!.loadPostsList(getOlder: false);
|
||||
}
|
||||
}
|
||||
|
@ -24,12 +24,12 @@ class AcceptedFriendTile extends StatelessWidget {
|
||||
final OnOpenPrivateConversation onOpenPrivateConversation;
|
||||
|
||||
const AcceptedFriendTile({
|
||||
Key key,
|
||||
@required this.friend,
|
||||
@required this.user,
|
||||
@required this.onRequestDelete,
|
||||
@required this.onSetFollowing,
|
||||
@required this.onOpenPrivateConversation,
|
||||
Key? key,
|
||||
required this.friend,
|
||||
required this.user,
|
||||
required this.onRequestDelete,
|
||||
required this.onSetFollowing,
|
||||
required this.onOpenPrivateConversation,
|
||||
}) : assert(friend != null),
|
||||
assert(user != null),
|
||||
assert(onRequestDelete != null),
|
||||
@ -40,24 +40,24 @@ class AcceptedFriendTile extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ListTile(
|
||||
onTap: () => openUserPage(context: context, userID: user.id),
|
||||
onTap: () => openUserPage(context: context, userID: user.id!),
|
||||
leading: AccountImageWidget(user: user),
|
||||
title: Text(user.displayName),
|
||||
subtitle: friend.isConnected
|
||||
? Text(
|
||||
tr(
|
||||
"Online",
|
||||
),
|
||||
)!,
|
||||
style: TextStyle(color: Colors.green),
|
||||
)
|
||||
: Text(
|
||||
diffTimeFromNowToStr(friend.lastActive),
|
||||
diffTimeFromNowToStr(friend.lastActive!)!,
|
||||
),
|
||||
trailing: PopupMenuButton<_FriendMenuChoices>(
|
||||
itemBuilder: (c) => [
|
||||
//Open a private conversation
|
||||
PopupMenuItem(
|
||||
child: Text(tr("Private conversation")),
|
||||
child: Text(tr("Private conversation")!),
|
||||
value: _FriendMenuChoices.PRIVATE_CONVERSATION,
|
||||
),
|
||||
|
||||
@ -69,7 +69,7 @@ class AcceptedFriendTile extends StatelessWidget {
|
||||
? Icons.check_box
|
||||
: Icons.check_box_outline_blank),
|
||||
Container(width: 10.0),
|
||||
Text(friend.following ? tr("Following") : tr("Follow")),
|
||||
Text(friend.following ? tr("Following")! : tr("Follow")!),
|
||||
],
|
||||
),
|
||||
value: _FriendMenuChoices.TOGGLE_FOLLOWING,
|
||||
@ -77,7 +77,7 @@ class AcceptedFriendTile extends StatelessWidget {
|
||||
|
||||
// Remove the friend from the list
|
||||
PopupMenuItem(
|
||||
child: Text(tr("Remove")),
|
||||
child: Text(tr("Remove")!),
|
||||
value: _FriendMenuChoices.REMOVE,
|
||||
),
|
||||
],
|
||||
|
@ -22,11 +22,11 @@ class CommentTile extends StatelessWidget {
|
||||
final void Function(Comment) onDeleteComment;
|
||||
|
||||
const CommentTile({
|
||||
Key key,
|
||||
@required this.comment,
|
||||
@required this.user,
|
||||
@required this.onUpdateComment,
|
||||
@required this.onDeleteComment,
|
||||
Key? key,
|
||||
required this.comment,
|
||||
required this.user,
|
||||
required this.onUpdateComment,
|
||||
required this.onDeleteComment,
|
||||
}) : assert(comment != null),
|
||||
assert(user != null),
|
||||
assert(onUpdateComment != null),
|
||||
@ -42,7 +42,7 @@ class CommentTile extends StatelessWidget {
|
||||
),
|
||||
subtitle: _buildCommentContent(),
|
||||
trailing: Text(
|
||||
diffTimeFromNowToStr(comment.timeSent),
|
||||
diffTimeFromNowToStr(comment.timeSent)!,
|
||||
style: TextStyle(fontSize: 10.0),
|
||||
),
|
||||
);
|
||||
@ -58,14 +58,14 @@ class CommentTile extends StatelessWidget {
|
||||
// Update comment content
|
||||
PopupMenuItem(
|
||||
enabled: comment.isOwner,
|
||||
child: Text(tr("Update")),
|
||||
child: Text(tr("Update")!),
|
||||
value: _CommentAction.UPDATE,
|
||||
),
|
||||
|
||||
// Delete comment
|
||||
PopupMenuItem(
|
||||
enabled: comment.isOwner,
|
||||
child: Text(tr("Delete")),
|
||||
child: Text(tr("Delete")!),
|
||||
value: _CommentAction.DELETE,
|
||||
),
|
||||
],
|
||||
@ -80,7 +80,7 @@ class CommentTile extends StatelessWidget {
|
||||
Container(
|
||||
child: comment.hasImage
|
||||
? NetworkImageWidget(
|
||||
url: comment.imageURL,
|
||||
url: comment.imageURL!,
|
||||
allowFullScreen: true,
|
||||
height: 100.0,
|
||||
width: null,
|
||||
|
@ -31,12 +31,12 @@ class ConversationMessageTile extends StatelessWidget {
|
||||
final OnRequestMessageDelete onRequestMessageDelete;
|
||||
|
||||
const ConversationMessageTile({
|
||||
Key key,
|
||||
@required this.message,
|
||||
@required this.user,
|
||||
@required this.onRequestMessageStats,
|
||||
@required this.onRequestMessageUpdate,
|
||||
@required this.onRequestMessageDelete,
|
||||
Key? key,
|
||||
required this.message,
|
||||
required this.user,
|
||||
required this.onRequestMessageStats,
|
||||
required this.onRequestMessageUpdate,
|
||||
required this.onRequestMessageDelete,
|
||||
}) : assert(message != null),
|
||||
assert(user != null),
|
||||
assert(onRequestMessageStats != null),
|
||||
@ -66,34 +66,34 @@ class ConversationMessageTile extends StatelessWidget {
|
||||
PopupMenuItem(
|
||||
enabled: (message.message?.content ?? "") != "",
|
||||
value: _MenuChoices.COPY_MESSAGE,
|
||||
child: Text(tr("Copy message")),
|
||||
child: Text(tr("Copy message")!),
|
||||
),
|
||||
|
||||
PopupMenuItem(
|
||||
enabled: message.file != null,
|
||||
value: _MenuChoices.COPY_URL,
|
||||
child: Text(tr("Copy URL")),
|
||||
child: Text(tr("Copy URL")!),
|
||||
),
|
||||
|
||||
PopupMenuItem(
|
||||
value: _MenuChoices.GET_STATS,
|
||||
child: Text(tr("Statistics")),
|
||||
child: Text(tr("Statistics")!),
|
||||
),
|
||||
|
||||
// Update message content
|
||||
PopupMenuItem(
|
||||
enabled: message.isOwner &&
|
||||
message.message != null &&
|
||||
message.message.content.isNotEmpty,
|
||||
message.message.content!.isNotEmpty,
|
||||
value: _MenuChoices.REQUEST_UPDATE_CONTENT,
|
||||
child: Text(tr("Update")),
|
||||
child: Text(tr("Update")!),
|
||||
),
|
||||
|
||||
// Delete the message
|
||||
PopupMenuItem(
|
||||
enabled: message.isOwner,
|
||||
value: _MenuChoices.DELETE,
|
||||
child: Text(tr("Delete")),
|
||||
child: Text(tr("Delete")!),
|
||||
),
|
||||
]..removeWhere((element) => !element.enabled),
|
||||
),
|
||||
@ -113,18 +113,18 @@ class ConversationMessageTile extends StatelessWidget {
|
||||
linksColor: Colors.white,
|
||||
);
|
||||
|
||||
return ConversationFileWidget(messageID: message.id, file: message.file);
|
||||
return ConversationFileWidget(messageID: message.id!, file: message.file!);
|
||||
}
|
||||
|
||||
/// Process menu choice
|
||||
void _menuOptionSelected(BuildContext context, _MenuChoices value) {
|
||||
switch (value) {
|
||||
case _MenuChoices.COPY_MESSAGE:
|
||||
copyToClipboard(context, message.message.content);
|
||||
copyToClipboard(context, message.message.content!);
|
||||
break;
|
||||
|
||||
case _MenuChoices.COPY_URL:
|
||||
copyToClipboard(context, message.file.url);
|
||||
copyToClipboard(context, message.file!.url!);
|
||||
break;
|
||||
|
||||
case _MenuChoices.GET_STATS:
|
||||
|
@ -23,19 +23,19 @@ enum _PopupMenuChoices { UPDATE, LEAVE }
|
||||
class ConversationTile extends StatelessWidget {
|
||||
final Conversation conversation;
|
||||
final UsersList usersList;
|
||||
final GroupsList groupsList;
|
||||
final GroupsList? groupsList;
|
||||
final OpenConversationCallback onOpen;
|
||||
final RequestUpdateConversationCallback onRequestUpdate;
|
||||
final RequestLeaveConversationCallback onRequestLeave;
|
||||
|
||||
const ConversationTile({
|
||||
Key key,
|
||||
@required this.conversation,
|
||||
@required this.usersList,
|
||||
@required this.groupsList,
|
||||
@required this.onOpen,
|
||||
@required this.onRequestUpdate,
|
||||
@required this.onRequestLeave,
|
||||
Key? key,
|
||||
required this.conversation,
|
||||
required this.usersList,
|
||||
required this.groupsList,
|
||||
required this.onOpen,
|
||||
required this.onRequestUpdate,
|
||||
required this.onRequestLeave,
|
||||
}) : assert(conversation != null),
|
||||
assert(usersList != null),
|
||||
assert(onOpen != null),
|
||||
@ -68,7 +68,7 @@ class ConversationTile extends StatelessWidget {
|
||||
ConversationsHelper.getConversationName(
|
||||
conversation,
|
||||
usersList,
|
||||
),
|
||||
)!,
|
||||
style: TextStyle(
|
||||
fontWeight: conversation.sawLastMessage ? null : FontWeight.bold,
|
||||
),
|
||||
@ -87,7 +87,7 @@ class ConversationTile extends StatelessWidget {
|
||||
conversation: conversation,
|
||||
users: usersList,
|
||||
group: conversation.isGroupConversation
|
||||
? groupsList.getGroup(conversation.groupID)
|
||||
? groupsList!.getGroup(conversation.groupID)
|
||||
: null,
|
||||
),
|
||||
|
||||
@ -97,24 +97,24 @@ class ConversationTile extends StatelessWidget {
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: <Widget>[
|
||||
_buildSubInformation(Icons.access_time,
|
||||
diffTimeFromNowToStr(conversation.lastActivity)),
|
||||
diffTimeFromNowToStr(conversation.lastActivity!)!),
|
||||
conversation.isGroupConversation
|
||||
? _buildSubInformation(
|
||||
Icons.link,
|
||||
tr("Group: %group_name%", args: {
|
||||
"group_name":
|
||||
groupsList.getGroup(conversation.groupID).name
|
||||
}))
|
||||
groupsList!.getGroup(conversation.groupID)!.name
|
||||
})!)
|
||||
: _buildSubInformation(
|
||||
Icons.group,
|
||||
conversation.members.length == 1
|
||||
? tr("1 member")
|
||||
conversation.members!.length == 1
|
||||
? tr("1 member")!
|
||||
: tr(
|
||||
"%num% members",
|
||||
args: {
|
||||
"num": conversation.members.length.toString(),
|
||||
"num": conversation.members!.length.toString(),
|
||||
},
|
||||
),
|
||||
)!,
|
||||
),
|
||||
],
|
||||
),
|
||||
@ -125,11 +125,11 @@ class ConversationTile extends StatelessWidget {
|
||||
position: position,
|
||||
items: [
|
||||
PopupMenuItem(
|
||||
child: Text(tr("Update")),
|
||||
child: Text(tr("Update")!),
|
||||
value: _PopupMenuChoices.UPDATE,
|
||||
),
|
||||
PopupMenuItem(
|
||||
child: Text(tr("Leave")),
|
||||
child: Text(tr("Leave")!),
|
||||
value: _PopupMenuChoices.LEAVE,
|
||||
)
|
||||
]).then(_conversationMenuCallback);
|
||||
@ -143,9 +143,9 @@ class ConversationTile extends StatelessWidget {
|
||||
return Padding(
|
||||
padding: EdgeInsets.only(bottom: 20),
|
||||
child: ListTile(
|
||||
onTap: () => MainController.of(context).startCall(conversation.id),
|
||||
onTap: () => MainController.of(context)!.startCall(conversation.id!),
|
||||
dense: true,
|
||||
title: Text(tr("Ongoing call")),
|
||||
title: Text(tr("Ongoing call")!),
|
||||
leading: Icon(Icons.call),
|
||||
tileColor: Colors.yellow.withOpacity(0.2),
|
||||
),
|
||||
@ -153,7 +153,7 @@ class ConversationTile extends StatelessWidget {
|
||||
}
|
||||
|
||||
/// Method called each time an option of the menu is selected
|
||||
void _conversationMenuCallback(_PopupMenuChoices c) {
|
||||
void _conversationMenuCallback(_PopupMenuChoices? c) {
|
||||
switch (c) {
|
||||
case _PopupMenuChoices.UPDATE:
|
||||
onRequestUpdate(conversation);
|
||||
|
@ -6,9 +6,9 @@ import 'package:flutter/material.dart';
|
||||
|
||||
class MenuTile extends StatelessWidget {
|
||||
final String title;
|
||||
final GestureTapCallback onTap;
|
||||
final GestureTapCallback? onTap;
|
||||
|
||||
const MenuTile({@required this.title, this.onTap}) : assert(title != null);
|
||||
const MenuTile({required this.title, this.onTap}) : assert(title != null);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
@ -16,10 +16,10 @@ class PendingFriendTile extends StatelessWidget {
|
||||
final RespondFriendshipRequestCallback onRespond;
|
||||
|
||||
const PendingFriendTile(
|
||||
{Key key,
|
||||
@required this.friend,
|
||||
@required this.user,
|
||||
@required this.onRespond})
|
||||
{Key? key,
|
||||
required this.friend,
|
||||
required this.user,
|
||||
required this.onRespond})
|
||||
: assert(friend != null),
|
||||
assert(user != null),
|
||||
assert(onRespond != null),
|
||||
@ -41,7 +41,7 @@ class PendingFriendTile extends StatelessWidget {
|
||||
children: <Widget>[
|
||||
ElevatedButton(
|
||||
child: Text(
|
||||
tr("Accept").toUpperCase(),
|
||||
tr("Accept")!.toUpperCase(),
|
||||
style: TextStyle(color: Colors.white),
|
||||
),
|
||||
style: ButtonStyle(
|
||||
@ -53,7 +53,7 @@ class PendingFriendTile extends StatelessWidget {
|
||||
),
|
||||
ElevatedButton(
|
||||
child: Text(
|
||||
tr("Reject").toUpperCase(),
|
||||
tr("Reject")!.toUpperCase(),
|
||||
style: TextStyle(color: Colors.white),
|
||||
),
|
||||
style: ButtonStyle(
|
||||
|
@ -52,13 +52,13 @@ class PostTile extends StatefulWidget {
|
||||
final bool userNamesClickable;
|
||||
|
||||
const PostTile({
|
||||
Key key,
|
||||
@required this.post,
|
||||
@required this.usersInfo,
|
||||
@required this.onDeletedPost,
|
||||
@required this.showPostTarget,
|
||||
@required this.groupsInfo,
|
||||
@required this.userNamesClickable,
|
||||
Key? key,
|
||||
required this.post,
|
||||
required this.usersInfo,
|
||||
required this.onDeletedPost,
|
||||
required this.showPostTarget,
|
||||
required this.groupsInfo,
|
||||
required this.userNamesClickable,
|
||||
}) : assert(post != null),
|
||||
assert(usersInfo != null),
|
||||
assert(onDeletedPost != null),
|
||||
@ -78,7 +78,7 @@ class _PostTileState extends State<PostTile> {
|
||||
|
||||
// Class members
|
||||
TextEditingController _commentController = TextEditingController();
|
||||
BytesFile _commentImage;
|
||||
BytesFile? _commentImage;
|
||||
bool _submitting = false;
|
||||
int _maxNumberOfCommentToShow = 10;
|
||||
|
||||
@ -99,7 +99,7 @@ class _PostTileState extends State<PostTile> {
|
||||
|
||||
return " > " +
|
||||
(widget.post.isGroupPost
|
||||
? widget.groupsInfo[widget.post.groupID].displayName
|
||||
? widget.groupsInfo[widget.post.groupID]!.displayName
|
||||
: widget.usersInfo.getUser(widget.post.userPageID).displayName);
|
||||
}
|
||||
|
||||
@ -114,7 +114,7 @@ class _PostTileState extends State<PostTile> {
|
||||
child: AccountImageWidget(user: _user),
|
||||
onTap: widget.userNamesClickable
|
||||
? () => openUserPage(
|
||||
userID: _user.id,
|
||||
userID: _user.id!,
|
||||
context: context,
|
||||
)
|
||||
: null,
|
||||
@ -130,7 +130,7 @@ class _PostTileState extends State<PostTile> {
|
||||
_user.displayName + _getPostTarget(),
|
||||
style: _userNameStyle,
|
||||
),
|
||||
Text(diffTimeFromNowToStr(widget.post.timeSent)),
|
||||
Text(diffTimeFromNowToStr(widget.post.timeSent)!),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -147,14 +147,14 @@ class _PostTileState extends State<PostTile> {
|
||||
itemBuilder: (c) => [
|
||||
// Update post content
|
||||
PopupMenuItem(
|
||||
child: Text(tr("Update content")),
|
||||
child: Text(tr("Update content")!),
|
||||
value: _PostActions.UPDATE_CONTENT,
|
||||
enabled: widget.post.canUpdate,
|
||||
),
|
||||
|
||||
// Delete post
|
||||
PopupMenuItem(
|
||||
child: Text(tr("Delete")),
|
||||
child: Text(tr("Delete")!),
|
||||
value: _PostActions.DELETE,
|
||||
enabled: widget.post.canDelete,
|
||||
),
|
||||
@ -166,7 +166,7 @@ class _PostTileState extends State<PostTile> {
|
||||
}
|
||||
|
||||
Widget _buildContentRow() {
|
||||
Widget postContent;
|
||||
Widget? postContent;
|
||||
switch (widget.post.kind) {
|
||||
case PostKind.IMAGE:
|
||||
postContent = _buildPostImage();
|
||||
@ -258,7 +258,7 @@ class _PostTileState extends State<PostTile> {
|
||||
|
||||
Widget _buildPostImage() {
|
||||
return NetworkImageWidget(
|
||||
url: widget.post.fileURL,
|
||||
url: widget.post.fileURL!,
|
||||
allowFullScreen: true,
|
||||
roundedEdges: false,
|
||||
loadingHeight: 150,
|
||||
@ -273,11 +273,11 @@ class _PostTileState extends State<PostTile> {
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Icon(Icons.ondemand_video),
|
||||
Text(tr("YouTube movie"))
|
||||
Text(tr("YouTube movie")!)
|
||||
],
|
||||
),
|
||||
onPressed: () =>
|
||||
launch("https://youtube.com/watch/?v=" + widget.post.filePath),
|
||||
launch("https://youtube.com/watch/?v=" + widget.post.filePath!),
|
||||
);
|
||||
}
|
||||
|
||||
@ -286,14 +286,14 @@ class _PostTileState extends State<PostTile> {
|
||||
color:
|
||||
darkTheme() ? darkerAccentColor : Color.fromRGBO(0xf7, 0xf7, 0xf7, 1),
|
||||
child: InkWell(
|
||||
onTap: () => launch(widget.post.linkURL),
|
||||
onTap: () => launch(widget.post.linkURL!),
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(right: 8.0),
|
||||
child: widget.post.hasLinkImage
|
||||
? NetworkImageWidget(
|
||||
url: widget.post.linkImage,
|
||||
url: widget.post.linkImage!,
|
||||
width: 70,
|
||||
roundedEdges: false,
|
||||
)
|
||||
@ -309,7 +309,7 @@ class _PostTileState extends State<PostTile> {
|
||||
Text(htmlDecodeCharacters(widget.post.linkTitle),
|
||||
style: TextStyle(fontSize: 20.0)),
|
||||
Text(
|
||||
widget.post.linkURL,
|
||||
widget.post.linkURL!,
|
||||
maxLines: 3,
|
||||
),
|
||||
Text(htmlDecodeCharacters(widget.post.linkDescription))
|
||||
@ -325,24 +325,24 @@ class _PostTileState extends State<PostTile> {
|
||||
Widget _buildPostPDF() {
|
||||
return ElevatedButton.icon(
|
||||
onPressed: () {
|
||||
launch(widget.post.fileURL);
|
||||
launch(widget.post.fileURL!);
|
||||
},
|
||||
icon: Icon(Icons.picture_as_pdf),
|
||||
label: Text(tr("PDF")),
|
||||
label: Text(tr("PDF")!),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildCountDownTimer() {
|
||||
return CountdownWidget(
|
||||
startTime: widget.post.timeSent,
|
||||
endTime: widget.post.timeEnd,
|
||||
endTime: widget.post.timeEnd!,
|
||||
);
|
||||
}
|
||||
|
||||
/// Build post survey
|
||||
Widget _buildPostSurvey() {
|
||||
return SurveyWidget(
|
||||
survey: widget.post.survey,
|
||||
survey: widget.post.survey!,
|
||||
onUpdated: (s) => setState(() => widget.post.survey = s),
|
||||
);
|
||||
}
|
||||
@ -352,11 +352,11 @@ class _PostTileState extends State<PostTile> {
|
||||
assert(widget.post.hasComments);
|
||||
|
||||
final comments = List<Widget>.generate(
|
||||
min(widget.post.comments.length, _maxNumberOfCommentToShow),
|
||||
min(widget.post.comments!.length, _maxNumberOfCommentToShow),
|
||||
(num) {
|
||||
final index = num +
|
||||
max(0, widget.post.comments.length - _maxNumberOfCommentToShow);
|
||||
final comment = widget.post.comments[index];
|
||||
max(0, widget.post.comments!.length - _maxNumberOfCommentToShow);
|
||||
final comment = widget.post.comments![index as int];
|
||||
return CommentTile(
|
||||
comment: comment,
|
||||
user: widget.usersInfo.getUser(comment.userID),
|
||||
@ -374,7 +374,7 @@ class _PostTileState extends State<PostTile> {
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(top: 8.0, bottom: 8.0),
|
||||
child: Column(
|
||||
children: (widget.post.comments.length > _maxNumberOfCommentToShow
|
||||
children: (widget.post.comments!.length > _maxNumberOfCommentToShow
|
||||
? [_buildShowMoreCommentsButton()]
|
||||
: [])
|
||||
..addAll(comments),
|
||||
@ -389,7 +389,7 @@ class _PostTileState extends State<PostTile> {
|
||||
onTap: () => setState(() => _maxNumberOfCommentToShow += 10),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(tr("Show more comments").toUpperCase()),
|
||||
child: Text(tr("Show more comments")!.toUpperCase()),
|
||||
),
|
||||
),
|
||||
);
|
||||
@ -400,7 +400,7 @@ class _PostTileState extends State<PostTile> {
|
||||
padding: EdgeInsets.only(
|
||||
left: 8.0,
|
||||
right: 8.0,
|
||||
top: (widget.post.comments.length > 0 ? 8.0 : 0)),
|
||||
top: (widget.post.comments!.length > 0 ? 8.0 : 0)),
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
// Comment input
|
||||
@ -502,7 +502,7 @@ class _PostTileState extends State<PostTile> {
|
||||
});
|
||||
} catch (e, s) {
|
||||
logError(e, s);
|
||||
snack(context, tr("Failed to choose an image!"));
|
||||
snack(context, tr("Failed to choose an image!")!);
|
||||
}
|
||||
}
|
||||
|
||||
@ -515,7 +515,7 @@ class _PostTileState extends State<PostTile> {
|
||||
postID: widget.post.id,
|
||||
content: _commentController.text,
|
||||
image: _commentImage,
|
||||
));
|
||||
)) ;
|
||||
|
||||
_sendingComment = false;
|
||||
|
||||
@ -524,7 +524,7 @@ class _PostTileState extends State<PostTile> {
|
||||
clearCommentForm();
|
||||
} catch (e) {
|
||||
print(e);
|
||||
showSimpleSnack(context, tr("Could not create comment!"));
|
||||
showSimpleSnack(context, tr("Could not create comment!")!);
|
||||
}
|
||||
}
|
||||
|
||||
@ -532,14 +532,14 @@ class _PostTileState extends State<PostTile> {
|
||||
Future<void> _updateCommentContent(Comment comment) async {
|
||||
final newContent = await askUserString(
|
||||
context: context,
|
||||
title: tr("Update comment content"),
|
||||
message: tr("New content:"),
|
||||
defaultValue: comment.content.isNull ? "" : comment.content.content,
|
||||
hint: tr("New content..."),
|
||||
title: tr("Update comment content")!,
|
||||
message: tr("New content:")!,
|
||||
defaultValue: comment.content.isNull ? "" : comment.content.content!,
|
||||
hint: tr("New content...")!,
|
||||
);
|
||||
|
||||
if (!(await _commentsHelper.updateContent(comment.id, newContent)))
|
||||
return showSimpleSnack(context, tr("Could not update comment content!"));
|
||||
return showSimpleSnack(context, tr("Could not update comment content!")!);
|
||||
}
|
||||
|
||||
/// Process the deletion of a user
|
||||
@ -550,7 +550,7 @@ class _PostTileState extends State<PostTile> {
|
||||
title: tr("Delete comment"))) return;
|
||||
|
||||
if (!await _commentsHelper.delete(comment.id)) {
|
||||
showSimpleSnack(context, tr("Could not delete the comment!"));
|
||||
showSimpleSnack(context, tr("Could not delete the comment!")!);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -582,7 +582,7 @@ class _PostTileState extends State<PostTile> {
|
||||
|
||||
// Update post visibility
|
||||
if (!await _postsHelper.setVisibility(widget.post.id, newLevel)) {
|
||||
showSimpleSnack(context, tr("Could not update post visibility!"));
|
||||
showSimpleSnack(context, tr("Could not update post visibility!")!);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -593,17 +593,17 @@ class _PostTileState extends State<PostTile> {
|
||||
Future<void> updateContent() async {
|
||||
final newContent = await askUserString(
|
||||
context: context,
|
||||
title: tr("Update post content"),
|
||||
message: tr("Please enter message content: "),
|
||||
title: tr("Update post content")!,
|
||||
message: tr("Please enter message content: ")!,
|
||||
defaultValue:
|
||||
widget.post.content.isNull ? "" : widget.post.content.content,
|
||||
hint: tr("Post content"),
|
||||
widget.post.content.isNull ? "" : widget.post.content.content!,
|
||||
hint: tr("Post content")!,
|
||||
);
|
||||
|
||||
if (newContent == null) return;
|
||||
|
||||
if (!await _postsHelper.updateContent(widget.post.id, newContent)) {
|
||||
showSimpleSnack(context, tr("Could not update post content!"));
|
||||
showSimpleSnack(context, tr("Could not update post content!")!);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -620,7 +620,7 @@ class _PostTileState extends State<PostTile> {
|
||||
)) return;
|
||||
|
||||
if (!await _postsHelper.delete(widget.post.id)) {
|
||||
showSimpleSnack(context, tr("Could not delete the post!"));
|
||||
showSimpleSnack(context, tr("Could not delete the post!")!);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -12,10 +12,10 @@ class PostVisibilityLevelTile extends StatelessWidget {
|
||||
final bool visible;
|
||||
|
||||
const PostVisibilityLevelTile({
|
||||
Key key,
|
||||
@required this.level,
|
||||
@required this.title,
|
||||
@required this.onSelect,
|
||||
Key? key,
|
||||
required this.level,
|
||||
required this.title,
|
||||
required this.onSelect,
|
||||
this.visible = true,
|
||||
}) : assert(level != null),
|
||||
assert(title != null),
|
||||
|
@ -11,9 +11,9 @@ class ServerConversationMessageTile extends StatelessWidget {
|
||||
final UsersList users;
|
||||
|
||||
const ServerConversationMessageTile({
|
||||
Key key,
|
||||
@required this.message,
|
||||
@required this.users,
|
||||
Key? key,
|
||||
required this.message,
|
||||
required this.users,
|
||||
}) : assert(message != null),
|
||||
assert(users != null),
|
||||
super(key: key);
|
||||
@ -22,7 +22,7 @@ class ServerConversationMessageTile extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
return Center(
|
||||
child: Text(
|
||||
message.getText(users),
|
||||
message.getText(users)!,
|
||||
style: TextStyle(
|
||||
fontStyle: FontStyle.italic,
|
||||
fontSize: 12,
|
||||
|
@ -12,13 +12,13 @@ typedef OnUserTap = void Function(User);
|
||||
|
||||
class SimpleUserTile extends StatelessWidget {
|
||||
final User user;
|
||||
final OnUserTap onTap;
|
||||
final Widget trailing;
|
||||
final String subtitle;
|
||||
final OnUserTap? onTap;
|
||||
final Widget? trailing;
|
||||
final String? subtitle;
|
||||
|
||||
const SimpleUserTile({
|
||||
Key key,
|
||||
this.user,
|
||||
Key? key,
|
||||
required this.user,
|
||||
this.onTap,
|
||||
this.trailing,
|
||||
this.subtitle,
|
||||
@ -28,12 +28,12 @@ class SimpleUserTile extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ListTile(
|
||||
onTap: onTap == null ? null : () => onTap(user),
|
||||
onTap: onTap == null ? null : () => onTap!(user),
|
||||
leading: AccountImageWidget(
|
||||
user: user,
|
||||
),
|
||||
title: Text(user.fullName),
|
||||
subtitle: subtitle == null ? null : Text(subtitle),
|
||||
subtitle: subtitle == null ? null : Text(subtitle!),
|
||||
trailing: trailing,
|
||||
);
|
||||
}
|
||||
|
@ -15,9 +15,9 @@ class FriendshipStatusWidget extends StatefulWidget {
|
||||
final void Function() onFriendshipUpdated;
|
||||
|
||||
const FriendshipStatusWidget({
|
||||
Key key,
|
||||
@required this.status,
|
||||
@required this.onFriendshipUpdated,
|
||||
Key? key,
|
||||
required this.status,
|
||||
required this.onFriendshipUpdated,
|
||||
}) : assert(status != null),
|
||||
assert(onFriendshipUpdated != null),
|
||||
super(key: key);
|
||||
@ -48,7 +48,7 @@ class _FriendshipStatusWidgetState extends State<FriendshipStatusWidget> {
|
||||
// No request sent yet
|
||||
if (widget.status.noRequestExchanged) {
|
||||
return ElevatedButton(
|
||||
child: Text(tr("Send request").toUpperCase()),
|
||||
child: Text(tr("Send request")!.toUpperCase()),
|
||||
onPressed: () =>
|
||||
executeRequest(() => _friendsHelper.sendRequest(friendID)),
|
||||
);
|
||||
@ -58,7 +58,7 @@ class _FriendshipStatusWidgetState extends State<FriendshipStatusWidget> {
|
||||
if (widget.status.sentRequest) {
|
||||
return ElevatedButton(
|
||||
child: Text(
|
||||
tr("Cancel request").toUpperCase(),
|
||||
tr("Cancel request")!.toUpperCase(),
|
||||
style: WhiteTextColorStyle,
|
||||
),
|
||||
style:
|
||||
@ -74,7 +74,7 @@ class _FriendshipStatusWidgetState extends State<FriendshipStatusWidget> {
|
||||
children: <Widget>[
|
||||
ElevatedButton(
|
||||
child: Text(
|
||||
tr("Accept request").toUpperCase(),
|
||||
tr("Accept request")!.toUpperCase(),
|
||||
style: WhiteTextColorStyle,
|
||||
),
|
||||
style: ButtonStyle(
|
||||
@ -84,7 +84,7 @@ class _FriendshipStatusWidgetState extends State<FriendshipStatusWidget> {
|
||||
),
|
||||
ElevatedButton(
|
||||
child: Text(
|
||||
tr("Reject request").toUpperCase(),
|
||||
tr("Reject request")!.toUpperCase(),
|
||||
style: WhiteTextColorStyle,
|
||||
),
|
||||
style: ButtonStyle(
|
||||
@ -98,7 +98,7 @@ class _FriendshipStatusWidgetState extends State<FriendshipStatusWidget> {
|
||||
|
||||
// The two users are friends, offers to follow him
|
||||
return ElevatedButton(
|
||||
child: Text((widget.status.following ? tr("Following") : tr("Follow"))
|
||||
child: Text((widget.status.following ? tr("Following") : tr("Follow"))!
|
||||
.toUpperCase()),
|
||||
onPressed: () => executeRequest(() =>
|
||||
_friendsHelper.setFollowing(friendID, !widget.status.following)),
|
||||
@ -110,7 +110,7 @@ class _FriendshipStatusWidgetState extends State<FriendshipStatusWidget> {
|
||||
setSentRequest(true);
|
||||
|
||||
if (!await func())
|
||||
showSimpleSnack(context, tr("Could not update your membership!"));
|
||||
showSimpleSnack(context, tr("Could not update your membership!")!);
|
||||
|
||||
widget.onFriendshipUpdated();
|
||||
}
|
||||
|
@ -13,8 +13,8 @@ class AccountImageWidget extends StatelessWidget {
|
||||
final double width;
|
||||
|
||||
const AccountImageWidget({
|
||||
Key key,
|
||||
this.user,
|
||||
Key? key,
|
||||
required this.user,
|
||||
this.width = 35.0,
|
||||
}) : assert(user != null),
|
||||
super(key: key);
|
||||
@ -23,7 +23,7 @@ class AccountImageWidget extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
return Material(
|
||||
child: CachedNetworkImage(
|
||||
imageUrl: user.accountImageURL,
|
||||
imageUrl: user.accountImageURL!,
|
||||
width: width,
|
||||
height: width,
|
||||
fit: BoxFit.cover,
|
||||
|
@ -31,18 +31,18 @@ class AsyncScreenWidget extends StatefulWidget {
|
||||
/// Widget to use while we are refreshing
|
||||
///
|
||||
/// This widget is optional
|
||||
final Widget loadingWidget;
|
||||
final Widget? loadingWidget;
|
||||
|
||||
/// Widget to use in case of error
|
||||
///
|
||||
/// This widget is optional
|
||||
final Widget errorWidget;
|
||||
final Widget? errorWidget;
|
||||
|
||||
const AsyncScreenWidget({
|
||||
Key key,
|
||||
@required this.onReload,
|
||||
@required this.onBuild,
|
||||
@required this.errorMessage,
|
||||
Key? key,
|
||||
required this.onReload,
|
||||
required this.onBuild,
|
||||
required this.errorMessage,
|
||||
this.showOldDataWhileUpdating = false,
|
||||
this.loadingWidget,
|
||||
this.errorWidget,
|
||||
@ -76,7 +76,7 @@ class AsyncScreenWidgetState extends SafeState<AsyncScreenWidget> {
|
||||
MaterialButton(
|
||||
textColor: Colors.white,
|
||||
onPressed: () => refresh(),
|
||||
child: Text(tr("Try again").toUpperCase()),
|
||||
child: Text(tr("Try again")!.toUpperCase()),
|
||||
)
|
||||
]);
|
||||
|
||||
|
@ -10,14 +10,14 @@ import 'package:url_launcher/url_launcher.dart';
|
||||
bool _bannerDismissed = false;
|
||||
|
||||
class BannerWidget extends StatefulWidget {
|
||||
const BannerWidget({Key key}) : super(key: key);
|
||||
const BannerWidget({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
_BannerWidgetState createState() => _BannerWidgetState();
|
||||
}
|
||||
|
||||
class _BannerWidgetState extends State<BannerWidget> {
|
||||
Timer _timer;
|
||||
Timer? _timer;
|
||||
|
||||
bool get _shouldShowBanner => showBanner && !_bannerDismissed;
|
||||
|
||||
@ -26,22 +26,22 @@ class _BannerWidgetState extends State<BannerWidget> {
|
||||
}
|
||||
|
||||
void _openLink() {
|
||||
launch(srvConfig.banner.link);
|
||||
launch(srvConfig!.banner!.link!);
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
if (_shouldShowBanner && srvConfig.banner.expire != null) {
|
||||
if (_shouldShowBanner && srvConfig!.banner!.expire != null) {
|
||||
_timer = Timer(
|
||||
Duration(seconds: srvConfig.banner.expire - time()), _hideBanner);
|
||||
Duration(seconds: srvConfig!.banner!.expire! - time()), _hideBanner);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
if (_timer != null) _timer.cancel();
|
||||
if (_timer != null) _timer!.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@ -49,7 +49,7 @@ class _BannerWidgetState extends State<BannerWidget> {
|
||||
Widget build(BuildContext context) {
|
||||
if (!_shouldShowBanner) return Container();
|
||||
|
||||
final banner = srvConfig.banner;
|
||||
final banner = srvConfig!.banner!;
|
||||
return Card(
|
||||
color: banner.nature == BannerNature.Information
|
||||
? Colors.blue
|
||||
@ -72,8 +72,8 @@ class _BannerWidgetState extends State<BannerWidget> {
|
||||
Expanded(
|
||||
child: Text(
|
||||
banner.message.containsKey(shortLang)
|
||||
? banner.message[shortLang]
|
||||
: banner.message["en"],
|
||||
? banner.message[shortLang]!
|
||||
: banner.message["en"]!,
|
||||
style: TextStyle(color: Colors.white),
|
||||
),
|
||||
),
|
||||
@ -95,16 +95,16 @@ class _BannerWidgetState extends State<BannerWidget> {
|
||||
}
|
||||
|
||||
class BannerButton extends StatelessWidget {
|
||||
final Function() onPressed;
|
||||
final Widget icon;
|
||||
final Function()? onPressed;
|
||||
final Widget? icon;
|
||||
|
||||
const BannerButton({this.onPressed, this.icon, Key key}) : super(key: key);
|
||||
const BannerButton({this.onPressed, this.icon, Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return IconButton(
|
||||
onPressed: onPressed,
|
||||
icon: icon,
|
||||
icon: icon!,
|
||||
color: Colors.white,
|
||||
disabledColor: Colors.white,
|
||||
padding: EdgeInsets.all(1.0),
|
||||
|
@ -19,9 +19,9 @@ class ConversationFileWidget extends StatefulWidget {
|
||||
final ConversationMessageFile file;
|
||||
|
||||
const ConversationFileWidget({
|
||||
Key key,
|
||||
@required this.messageID,
|
||||
@required this.file,
|
||||
Key? key,
|
||||
required this.messageID,
|
||||
required this.file,
|
||||
}) : assert(messageID != null),
|
||||
assert(file != null),
|
||||
super(key: key);
|
||||
@ -44,7 +44,7 @@ class _ConversationFileWidgetState extends State<ConversationFileWidget> {
|
||||
: Opacity(
|
||||
opacity: 0.8,
|
||||
child: CachedNetworkImage(
|
||||
imageUrl: file.thumbnail,
|
||||
imageUrl: file.thumbnail!,
|
||||
width: _AreaWidth,
|
||||
height: _AreaHeight,
|
||||
fit: BoxFit.cover,
|
||||
@ -64,7 +64,7 @@ class _ConversationFileWidgetState extends State<ConversationFileWidget> {
|
||||
case ConversationMessageFileType.IMAGE:
|
||||
return Center(
|
||||
child: NetworkImageWidget(
|
||||
url: file.url,
|
||||
url: file.url!,
|
||||
thumbnailURL: file.thumbnail,
|
||||
allowFullScreen: true,
|
||||
),
|
||||
@ -83,9 +83,9 @@ class _ConversationFileWidgetState extends State<ConversationFileWidget> {
|
||||
Icon(file.icon, color: Colors.white),
|
||||
Spacer(),
|
||||
Text(
|
||||
file.name.length < 23
|
||||
? file.name
|
||||
: file.name.substring(0, 20) + "...",
|
||||
file.name!.length < 23
|
||||
? file.name!
|
||||
: file.name!.substring(0, 20) + "...",
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(color: Colors.white),
|
||||
),
|
||||
@ -112,15 +112,15 @@ class _ConversationFileWidgetState extends State<ConversationFileWidget> {
|
||||
break;
|
||||
|
||||
case ConversationMessageFileType.VIDEO:
|
||||
MainController.of(context).push(
|
||||
VideoPlayerRoute(url: file.url),
|
||||
MainController.of(context)!.push(
|
||||
VideoPlayerRoute(url: file.url!),
|
||||
hideNavBar: true,
|
||||
canShowAsDialog: true,
|
||||
);
|
||||
break;
|
||||
|
||||
default:
|
||||
launch(file.url);
|
||||
launch(file.url!);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -14,13 +14,13 @@ class ConversationImageWidget extends StatelessWidget {
|
||||
final Conversation conversation;
|
||||
final UsersList users;
|
||||
final double size;
|
||||
final Group group;
|
||||
final bool noUserImage;
|
||||
final Group? group;
|
||||
final bool? noUserImage;
|
||||
|
||||
const ConversationImageWidget({
|
||||
Key key,
|
||||
@required this.conversation,
|
||||
@required this.users,
|
||||
Key? key,
|
||||
required this.conversation,
|
||||
required this.users,
|
||||
this.group,
|
||||
this.size = 30,
|
||||
this.noUserImage,
|
||||
@ -42,26 +42,26 @@ class ConversationImageWidget extends StatelessWidget {
|
||||
Widget _buildIcon() {
|
||||
if (conversation.logoURL != null)
|
||||
return CachedNetworkImage(
|
||||
imageUrl: conversation.logoURL,
|
||||
imageUrl: conversation.logoURL!,
|
||||
width: size,
|
||||
);
|
||||
|
||||
if (group != null) {
|
||||
return CachedNetworkImage(
|
||||
imageUrl: group.iconURL,
|
||||
imageUrl: group!.iconURL,
|
||||
width: size,
|
||||
);
|
||||
}
|
||||
|
||||
if (noUserImage == true) return Container(width: size);
|
||||
|
||||
if (conversation.members.length < 2)
|
||||
if (conversation.members!.length < 2)
|
||||
return Icon(
|
||||
Icons.lock,
|
||||
size: size,
|
||||
);
|
||||
|
||||
if (conversation.members.length == 2)
|
||||
if (conversation.members!.length == 2)
|
||||
return AccountImageWidget(
|
||||
width: size,
|
||||
user: users.getUser(conversation.otherMembersID.first),
|
||||
@ -80,9 +80,9 @@ class MultipleAccountImagesWidget extends StatelessWidget {
|
||||
final double size;
|
||||
|
||||
const MultipleAccountImagesWidget({
|
||||
Key key,
|
||||
@required this.users,
|
||||
@required this.size,
|
||||
Key? key,
|
||||
required this.users,
|
||||
required this.size,
|
||||
}) : assert(users != null),
|
||||
assert(size != null),
|
||||
assert(size > 0),
|
||||
|
@ -18,7 +18,7 @@ class CopyIcon extends StatelessWidget {
|
||||
icon: Icon(Icons.content_copy),
|
||||
onPressed: () {
|
||||
FlutterClipboard.copy(value);
|
||||
snack(context, tr("'%c%' was copied to clipboard", args: {"c": value}));
|
||||
snack(context, tr("'%c%' was copied to clipboard", args: {"c": value})!);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
@ -13,9 +13,9 @@ class CountdownWidget extends StatefulWidget {
|
||||
final int endTime;
|
||||
|
||||
const CountdownWidget({
|
||||
Key key,
|
||||
@required this.startTime,
|
||||
@required this.endTime,
|
||||
Key? key,
|
||||
required this.startTime,
|
||||
required this.endTime,
|
||||
}) : assert(startTime != null),
|
||||
assert(endTime != null),
|
||||
super(key: key);
|
||||
@ -29,7 +29,7 @@ class _CountdownWidgetState extends State<CountdownWidget> {
|
||||
|
||||
int get totalDuration => (widget.endTime - widget.startTime).abs();
|
||||
|
||||
String get remainingTimeStr {
|
||||
String? get remainingTimeStr {
|
||||
final remaining = Duration(seconds: remainingTime.abs());
|
||||
|
||||
return tr(
|
||||
@ -67,7 +67,7 @@ class _CountdownWidgetState extends State<CountdownWidget> {
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(remainingTimeStr),
|
||||
child: Text(remainingTimeStr!),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
|
@ -17,7 +17,7 @@ import 'new_password_input_widget.dart';
|
||||
class CreateAccountWidget extends StatefulWidget {
|
||||
final void Function() onCreated;
|
||||
|
||||
const CreateAccountWidget({Key key, @required this.onCreated})
|
||||
const CreateAccountWidget({Key? key, required this.onCreated})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
@ -30,28 +30,28 @@ class _CreateAccountWidgetState extends State<CreateAccountWidget> {
|
||||
final _emailController = TextEditingController();
|
||||
final _passwordInputKey = GlobalKey<NewPasswordInputWidgetState>();
|
||||
final _verifyPasswordController = TextEditingController();
|
||||
bool _acceptedTOS = false;
|
||||
bool? _acceptedTOS = false;
|
||||
|
||||
bool _isCreating = false;
|
||||
CreateAccountResult _createAccountResult;
|
||||
CreateAccountResult? _createAccountResult;
|
||||
|
||||
bool _showErrors = false;
|
||||
|
||||
bool get _isFirstNameValid =>
|
||||
_firstNameController.text.length >=
|
||||
srvConfig.accountInformationPolicy.minFirstNameLength;
|
||||
srvConfig!.accountInformationPolicy.minFirstNameLength;
|
||||
|
||||
bool get _isLastNameValid =>
|
||||
_lastNameController.text.length >=
|
||||
srvConfig.accountInformationPolicy.minLastNameLength;
|
||||
srvConfig!.accountInformationPolicy.minLastNameLength;
|
||||
|
||||
bool get _isEmailValid => validateEmail(_emailController.text);
|
||||
|
||||
bool get _isPasswordValid => _passwordInputKey.currentState.valid;
|
||||
bool get _isPasswordValid => _passwordInputKey.currentState!.valid;
|
||||
|
||||
bool get _isPasswordConfirmationValid =>
|
||||
_passwordInputKey.currentState != null &&
|
||||
_passwordInputKey.currentState.value == _verifyPasswordController.text;
|
||||
_passwordInputKey.currentState!.value == _verifyPasswordController.text;
|
||||
|
||||
bool get _isFormValid =>
|
||||
_isFirstNameValid &&
|
||||
@ -59,9 +59,9 @@ class _CreateAccountWidgetState extends State<CreateAccountWidget> {
|
||||
_isEmailValid &&
|
||||
_isPasswordValid &&
|
||||
_isPasswordConfirmationValid &&
|
||||
_acceptedTOS;
|
||||
_acceptedTOS!;
|
||||
|
||||
String get errorMessage => _createAccountResult ==
|
||||
String? get errorMessage => _createAccountResult ==
|
||||
CreateAccountResult.ERROR_EXISTING_EMAIL
|
||||
? tr("An account is already associated to this email address!")
|
||||
: _createAccountResult == CreateAccountResult.ERROR_TOO_MANY_REQUESTS
|
||||
@ -91,11 +91,11 @@ class _CreateAccountWidgetState extends State<CreateAccountWidget> {
|
||||
// First name
|
||||
_InputEntry(
|
||||
controller: _firstNameController,
|
||||
label: tr("First name"),
|
||||
label: tr("First name")!,
|
||||
onEdited: _updateUI,
|
||||
icon: Icon(Icons.perm_identity),
|
||||
maxLength:
|
||||
srvConfig.accountInformationPolicy.maxFirstNameLength,
|
||||
srvConfig!.accountInformationPolicy.maxFirstNameLength,
|
||||
error: _showErrors && !_isFirstNameValid
|
||||
? tr("Invalid first name!")
|
||||
: null,
|
||||
@ -104,10 +104,10 @@ class _CreateAccountWidgetState extends State<CreateAccountWidget> {
|
||||
// Last name
|
||||
_InputEntry(
|
||||
controller: _lastNameController,
|
||||
label: tr("Last name"),
|
||||
label: tr("Last name")!,
|
||||
onEdited: _updateUI,
|
||||
icon: Icon(Icons.perm_identity),
|
||||
maxLength: srvConfig.accountInformationPolicy.maxLastNameLength,
|
||||
maxLength: srvConfig!.accountInformationPolicy.maxLastNameLength,
|
||||
error: _showErrors && !_isLastNameValid
|
||||
? tr("Invalid last name!")
|
||||
: null,
|
||||
@ -116,7 +116,7 @@ class _CreateAccountWidgetState extends State<CreateAccountWidget> {
|
||||
// Email address
|
||||
_InputEntry(
|
||||
controller: _emailController,
|
||||
label: tr("Email address"),
|
||||
label: tr("Email address")!,
|
||||
onEdited: _updateUI,
|
||||
icon: Icon(Icons.email),
|
||||
keyboard: TextInputType.emailAddress,
|
||||
@ -128,7 +128,7 @@ class _CreateAccountWidgetState extends State<CreateAccountWidget> {
|
||||
// Password
|
||||
NewPasswordInputWidget(
|
||||
key: _passwordInputKey,
|
||||
label: tr("Password"),
|
||||
label: tr("Password")!,
|
||||
onEdited: _updateUI,
|
||||
icon: Icon(Icons.lock),
|
||||
user: UserInfoForPassword(
|
||||
@ -141,7 +141,7 @@ class _CreateAccountWidgetState extends State<CreateAccountWidget> {
|
||||
// Verify password
|
||||
_InputEntry(
|
||||
controller: _verifyPasswordController,
|
||||
label: tr("Confirm your password"),
|
||||
label: tr("Confirm your password")!,
|
||||
onEdited: _updateUI,
|
||||
icon: Icon(Icons.lock_outline),
|
||||
isPassword: true,
|
||||
@ -155,15 +155,15 @@ class _CreateAccountWidgetState extends State<CreateAccountWidget> {
|
||||
// TOS
|
||||
CheckboxListTile(
|
||||
title:
|
||||
Text(tr("I have read and accepted the Terms Of Service.")),
|
||||
Text(tr("I have read and accepted the Terms Of Service.")!),
|
||||
value: _acceptedTOS,
|
||||
onChanged: (b) {
|
||||
_acceptedTOS = b;
|
||||
_updateUI();
|
||||
},
|
||||
subtitle: _showErrors && !_acceptedTOS
|
||||
subtitle: _showErrors && !_acceptedTOS!
|
||||
? Text(
|
||||
tr("You must accept the Terms Of Service to continue."),
|
||||
tr("You must accept the Terms Of Service to continue.")!,
|
||||
style: TextStyle(color: Colors.redAccent),
|
||||
)
|
||||
: null,
|
||||
@ -180,7 +180,7 @@ class _CreateAccountWidgetState extends State<CreateAccountWidget> {
|
||||
Center(
|
||||
child: ElevatedButton(
|
||||
onPressed: _submitForm,
|
||||
child: Text(tr("Create account")),
|
||||
child: Text(tr("Create account")!),
|
||||
),
|
||||
),
|
||||
],
|
||||
@ -211,7 +211,7 @@ class _CreateAccountWidgetState extends State<CreateAccountWidget> {
|
||||
firstName: _firstNameController.text,
|
||||
lastName: _lastNameController.text,
|
||||
email: _emailController.text,
|
||||
password: _passwordInputKey.currentState.value,
|
||||
password: _passwordInputKey.currentState!.value,
|
||||
));
|
||||
|
||||
setState(() {
|
||||
@ -228,17 +228,17 @@ class _CreateAccountWidgetState extends State<CreateAccountWidget> {
|
||||
widget.onCreated();
|
||||
}
|
||||
|
||||
void _openTOS() => launch(ServerConfigurationHelper.config.termsURL);
|
||||
void _openTOS() => launch(ServerConfigurationHelper.config!.termsURL);
|
||||
|
||||
void _showCreateAccountError() async {
|
||||
await showCupertinoDialog(
|
||||
context: context,
|
||||
builder: (c) => CupertinoAlertDialog(
|
||||
title: Text(tr("Error while creating your account")),
|
||||
content: Text(errorMessage),
|
||||
title: Text(tr("Error while creating your account")!),
|
||||
content: Text(errorMessage!),
|
||||
actions: <Widget>[
|
||||
CupertinoButton(
|
||||
child: Text(tr("Ok").toUpperCase()),
|
||||
child: Text(tr("Ok")!.toUpperCase()),
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
)
|
||||
],
|
||||
@ -252,16 +252,16 @@ class _InputEntry extends StatelessWidget {
|
||||
final String label;
|
||||
final VoidCallback onEdited;
|
||||
final bool isPassword;
|
||||
final String error;
|
||||
final Widget icon;
|
||||
final TextInputType keyboard;
|
||||
final int maxLength;
|
||||
final String? error;
|
||||
final Widget? icon;
|
||||
final TextInputType? keyboard;
|
||||
final int? maxLength;
|
||||
|
||||
const _InputEntry({
|
||||
Key key,
|
||||
@required this.controller,
|
||||
@required this.label,
|
||||
@required this.onEdited,
|
||||
Key? key,
|
||||
required this.controller,
|
||||
required this.label,
|
||||
required this.onEdited,
|
||||
this.isPassword = false,
|
||||
this.error,
|
||||
this.icon,
|
||||
|
@ -9,7 +9,7 @@ class AppBarWrapper extends StatelessWidget implements PreferredSizeWidget {
|
||||
final Widget appBar;
|
||||
final double height;
|
||||
|
||||
const AppBarWrapper({@required this.height, @required this.appBar})
|
||||
const AppBarWrapper({required this.height, required this.appBar})
|
||||
: assert(height != null),
|
||||
assert(appBar != null),
|
||||
super();
|
||||
|
@ -6,27 +6,27 @@ import 'package:flutter/material.dart';
|
||||
/// @author Pierre HUBERT
|
||||
|
||||
class CustomListTile extends StatelessWidget {
|
||||
final Widget leading;
|
||||
final Widget title;
|
||||
final Widget subtitle;
|
||||
final Widget trailing;
|
||||
final Widget? leading;
|
||||
final Widget? title;
|
||||
final Widget? subtitle;
|
||||
final Widget? trailing;
|
||||
final bool isThreeLine;
|
||||
final bool dense;
|
||||
final EdgeInsetsGeometry contentPadding;
|
||||
final bool? dense;
|
||||
final EdgeInsetsGeometry? contentPadding;
|
||||
final bool enabled;
|
||||
final GestureTapCallback onTap;
|
||||
final GestureLongPressCallback onLongPress;
|
||||
final GestureTapCallback? onTap;
|
||||
final GestureLongPressCallback? onLongPress;
|
||||
final bool selected;
|
||||
final Color tileColor;
|
||||
final Color? tileColor;
|
||||
|
||||
/// Custom onLongPress function
|
||||
final Function(Size, Offset) onLongPressWithInfo;
|
||||
final Function(Size, Offset)? onLongPressWithInfo;
|
||||
|
||||
/// Show menu onLongPress
|
||||
final Function(RelativeRect) onLongPressOpenMenu;
|
||||
final Function(RelativeRect)? onLongPressOpenMenu;
|
||||
|
||||
const CustomListTile({
|
||||
Key key,
|
||||
Key? key,
|
||||
this.leading,
|
||||
this.title,
|
||||
this.subtitle,
|
||||
@ -66,14 +66,14 @@ class CustomListTile extends StatelessWidget {
|
||||
}
|
||||
|
||||
void _longPress(BuildContext context) {
|
||||
if (onLongPress != null) onLongPress();
|
||||
if (onLongPress != null) onLongPress!();
|
||||
|
||||
if (onLongPressWithInfo != null || onLongPressOpenMenu != null) {
|
||||
RenderBox renderBox = context.findRenderObject();
|
||||
RenderBox renderBox = context.findRenderObject() as RenderBox;
|
||||
final size = renderBox.size;
|
||||
final offset = renderBox.localToGlobal(Offset(size.width, size.height));
|
||||
|
||||
if (onLongPressWithInfo != null) onLongPressWithInfo(size, offset);
|
||||
if (onLongPressWithInfo != null) onLongPressWithInfo!(size, offset);
|
||||
|
||||
if (onLongPressOpenMenu != null) {
|
||||
final position = RelativeRect.fromLTRB(
|
||||
@ -83,7 +83,7 @@ class CustomListTile extends StatelessWidget {
|
||||
offset.dy + size.height,
|
||||
);
|
||||
|
||||
onLongPressOpenMenu(position);
|
||||
onLongPressOpenMenu!(position);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,8 +7,8 @@ class AutoSizeDialogContentWidget extends StatelessWidget {
|
||||
final Widget child;
|
||||
|
||||
const AutoSizeDialogContentWidget({
|
||||
Key key,
|
||||
@required this.child,
|
||||
Key? key,
|
||||
required this.child,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
|
@ -10,7 +10,7 @@ class CancelDialogButton extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialButton(
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
child: Text(tr("Cancel").toUpperCase()),
|
||||
child: Text(tr("Cancel")!.toUpperCase()),
|
||||
textColor: Colors.red,
|
||||
);
|
||||
}
|
||||
|
@ -10,9 +10,9 @@ class ConfirmDialogButton<T> extends StatelessWidget {
|
||||
final T value;
|
||||
|
||||
const ConfirmDialogButton({
|
||||
Key key,
|
||||
Key? key,
|
||||
this.enabled = true,
|
||||
@required this.value,
|
||||
required this.value,
|
||||
}) : assert(enabled != null),
|
||||
super(key: key);
|
||||
|
||||
@ -20,7 +20,7 @@ class ConfirmDialogButton<T> extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialButton(
|
||||
onPressed: enabled ? () => Navigator.of(context).pop(value) : null,
|
||||
child: Text(tr("Confirm").toUpperCase()),
|
||||
child: Text(tr("Confirm")!.toUpperCase()),
|
||||
textColor: Colors.green,
|
||||
);
|
||||
}
|
||||
|
@ -11,18 +11,18 @@ import 'package:table_calendar/table_calendar.dart';
|
||||
enum CalendarDisplayMode { SINGLE_USER, MULTIPLE_USERS }
|
||||
|
||||
extension DateOnlyCompare on DateTime {
|
||||
bool isSameDate(DateTime other) => date_utils.isSameDate(this, other);
|
||||
bool isSameDate(DateTime? other) => date_utils.isSameDate(this, other);
|
||||
}
|
||||
|
||||
class PresenceCalendarWidget extends StatefulWidget {
|
||||
final PresenceSet presenceSet;
|
||||
final void Function(DateTime) onDayClicked;
|
||||
final void Function(DateTime)? onDayClicked;
|
||||
final CalendarDisplayMode mode;
|
||||
final DateTime selectedDay;
|
||||
final DateTime? selectedDay;
|
||||
|
||||
const PresenceCalendarWidget({
|
||||
Key key,
|
||||
@required this.presenceSet,
|
||||
Key? key,
|
||||
required this.presenceSet,
|
||||
this.onDayClicked,
|
||||
this.mode = CalendarDisplayMode.SINGLE_USER,
|
||||
this.selectedDay,
|
||||
@ -35,7 +35,7 @@ class PresenceCalendarWidget extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _PresenceCalendarWidgetState extends State<PresenceCalendarWidget> {
|
||||
var selectedDay = DateTime.now();
|
||||
DateTime? selectedDay = DateTime.now();
|
||||
|
||||
@override
|
||||
void didUpdateWidget(covariant PresenceCalendarWidget oldWidget) {
|
||||
@ -63,7 +63,7 @@ class _PresenceCalendarWidgetState extends State<PresenceCalendarWidget> {
|
||||
),
|
||||
onDaySelected: _selectedDay,
|
||||
availableCalendarFormats: const {CalendarFormat.month: "Mois"},
|
||||
focusedDay: selectedDay,
|
||||
focusedDay: selectedDay!,
|
||||
onPageChanged: (s) {
|
||||
setState(() {
|
||||
selectedDay = s;
|
||||
@ -129,21 +129,21 @@ class _PresenceCalendarWidgetState extends State<PresenceCalendarWidget> {
|
||||
}
|
||||
|
||||
void _selectedDay(DateTime selecteDay, DateTime focusedDay) {
|
||||
if (widget.onDayClicked != null) widget.onDayClicked(selecteDay);
|
||||
if (widget.onDayClicked != null) widget.onDayClicked!(selecteDay);
|
||||
setState(() {});
|
||||
}
|
||||
}
|
||||
|
||||
class CellWidget extends StatelessWidget {
|
||||
final String text;
|
||||
final Color color;
|
||||
final Color textColor;
|
||||
final Color? color;
|
||||
final Color? textColor;
|
||||
final bool circle;
|
||||
final bool selected;
|
||||
final bool? selected;
|
||||
|
||||
const CellWidget({
|
||||
Key key,
|
||||
@required this.text,
|
||||
Key? key,
|
||||
required this.text,
|
||||
this.color,
|
||||
this.textColor,
|
||||
this.circle = true,
|
||||
|
@ -12,13 +12,13 @@ import 'package:flutter/material.dart';
|
||||
class GroupFollowingWidget extends StatefulWidget {
|
||||
final Group group;
|
||||
final Function() onUpdated;
|
||||
final Color inactiveColor;
|
||||
final Color activeColor;
|
||||
final Color? inactiveColor;
|
||||
final Color? activeColor;
|
||||
|
||||
const GroupFollowingWidget({
|
||||
Key key,
|
||||
@required this.group,
|
||||
@required this.onUpdated,
|
||||
Key? key,
|
||||
required this.group,
|
||||
required this.onUpdated,
|
||||
this.activeColor,
|
||||
this.inactiveColor,
|
||||
}) : assert(group != null),
|
||||
@ -46,7 +46,7 @@ class _GroupFollowingWidgetState extends SafeState<GroupFollowingWidget> {
|
||||
color: _group.following ? widget.activeColor : widget.inactiveColor,
|
||||
)),
|
||||
TextSpan(
|
||||
text: " " + (_group.following ? tr("Following") : tr("Follow"))),
|
||||
text: " " + (_group.following ? tr("Following")! : tr("Follow")!)),
|
||||
]),
|
||||
),
|
||||
onTap: () => _toggleFollowing(),
|
||||
@ -56,7 +56,7 @@ class _GroupFollowingWidgetState extends SafeState<GroupFollowingWidget> {
|
||||
/// Toggle following status
|
||||
void _toggleFollowing() async {
|
||||
if (!await GroupsHelper().setFollowing(_group.id, !_group.following)) {
|
||||
showSimpleSnack(context, tr("Could not update following status!"));
|
||||
showSimpleSnack(context, tr("Could not update following status!")!);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -10,8 +10,8 @@ class GroupIcon extends StatelessWidget {
|
||||
final double width;
|
||||
|
||||
const GroupIcon({
|
||||
Key key,
|
||||
@required this.group,
|
||||
Key? key,
|
||||
required this.group,
|
||||
this.width = 50,
|
||||
}) : assert(group != null),
|
||||
assert(width != null),
|
||||
|
@ -12,11 +12,11 @@ import 'package:flutter/material.dart';
|
||||
|
||||
class GroupMembershipWidget extends StatefulWidget {
|
||||
final Group group;
|
||||
final Function() onUpdated;
|
||||
final Function() onError;
|
||||
final Function()? onUpdated;
|
||||
final Function()? onError;
|
||||
|
||||
const GroupMembershipWidget(
|
||||
{@required this.group, this.onUpdated, this.onError})
|
||||
{required this.group, this.onUpdated, this.onError})
|
||||
: assert(group != null);
|
||||
|
||||
@override
|
||||
@ -34,13 +34,13 @@ class _GroupMembershipWidgetState extends SafeState<GroupMembershipWidget> {
|
||||
Widget build(BuildContext context) {
|
||||
switch (_level) {
|
||||
case GroupMembershipLevel.ADMINISTRATOR:
|
||||
return Text(tr("Administrator"));
|
||||
return Text(tr("Administrator")!);
|
||||
|
||||
case GroupMembershipLevel.MODERATOR:
|
||||
return Text(tr("Moderator"));
|
||||
return Text(tr("Moderator")!);
|
||||
|
||||
case GroupMembershipLevel.MEMBER:
|
||||
return Text(tr("Member"));
|
||||
return Text(tr("Member")!);
|
||||
|
||||
case GroupMembershipLevel.INVITED:
|
||||
return _buildInvitedState();
|
||||
@ -63,7 +63,7 @@ class _GroupMembershipWidgetState extends SafeState<GroupMembershipWidget> {
|
||||
WidgetSpan(
|
||||
child: Icon(Icons.info_outline, size: 12),
|
||||
alignment: PlaceholderAlignment.middle),
|
||||
TextSpan(text: " " + tr("Invited") + " ", style: blackForWhiteTheme()),
|
||||
TextSpan(text: " " + tr("Invited")! + " ", style: blackForWhiteTheme()),
|
||||
TextSpan(
|
||||
text: tr("Accept"),
|
||||
style: TextStyle(color: Colors.green),
|
||||
@ -88,15 +88,15 @@ class _GroupMembershipWidgetState extends SafeState<GroupMembershipWidget> {
|
||||
return;
|
||||
|
||||
if (!await GroupsHelper.respondInvitation(_id, accept)) {
|
||||
showSimpleSnack(context, tr("Could not respond to your invitation!"));
|
||||
if (this.widget.onError != null) this.widget.onError();
|
||||
showSimpleSnack(context, tr("Could not respond to your invitation!")!);
|
||||
if (this.widget.onError != null) this.widget.onError!();
|
||||
} else {
|
||||
// Refresh state
|
||||
group.membershipLevel =
|
||||
accept ? GroupMembershipLevel.MEMBER : GroupMembershipLevel.VISITOR;
|
||||
this.setState(() {});
|
||||
|
||||
if (this.widget.onUpdated != null) this.widget.onUpdated();
|
||||
if (this.widget.onUpdated != null) this.widget.onUpdated!();
|
||||
}
|
||||
}
|
||||
|
||||
@ -108,7 +108,7 @@ class _GroupMembershipWidgetState extends SafeState<GroupMembershipWidget> {
|
||||
child: Icon(Icons.access_time, size: 12),
|
||||
alignment: PlaceholderAlignment.middle),
|
||||
TextSpan(
|
||||
text: " " + tr("Requested") + " ", style: blackForWhiteTheme()),
|
||||
text: " " + tr("Requested")! + " ", style: blackForWhiteTheme()),
|
||||
TextSpan(
|
||||
text: tr("Cancel"),
|
||||
style: TextStyle(color: Colors.blue),
|
||||
@ -120,14 +120,14 @@ class _GroupMembershipWidgetState extends SafeState<GroupMembershipWidget> {
|
||||
/// Cancel group membership request
|
||||
void _cancelRequest() async {
|
||||
if (!await GroupsHelper().cancelRequest(_id)) {
|
||||
showSimpleSnack(context, tr("Could not cancel your membership request!"));
|
||||
if (this.widget.onError != null) this.widget.onError();
|
||||
showSimpleSnack(context, tr("Could not cancel your membership request!")!);
|
||||
if (this.widget.onError != null) this.widget.onError!();
|
||||
} else {
|
||||
// Refresh state
|
||||
group.membershipLevel = GroupMembershipLevel.VISITOR;
|
||||
this.setState(() {});
|
||||
|
||||
if (this.widget.onUpdated != null) this.widget.onUpdated();
|
||||
if (this.widget.onUpdated != null) this.widget.onUpdated!();
|
||||
}
|
||||
}
|
||||
|
||||
@ -135,7 +135,7 @@ class _GroupMembershipWidgetState extends SafeState<GroupMembershipWidget> {
|
||||
Widget _buildVisitorState() {
|
||||
// Check if the user can request membership
|
||||
if (group.registrationLevel == GroupRegistrationLevel.CLOSED)
|
||||
return Text(tr("Closed registration"));
|
||||
return Text(tr("Closed registration")!);
|
||||
|
||||
return RichText(
|
||||
text: TextSpan(
|
||||
@ -149,14 +149,14 @@ class _GroupMembershipWidgetState extends SafeState<GroupMembershipWidget> {
|
||||
/// Create new membership request
|
||||
void _requestMembership() async {
|
||||
if (!await GroupsHelper.sendRequest(_id)) {
|
||||
showSimpleSnack(context, tr("Could not send your membership request!"));
|
||||
if (this.widget.onError != null) this.widget.onError();
|
||||
showSimpleSnack(context, tr("Could not send your membership request!")!);
|
||||
if (this.widget.onError != null) this.widget.onError!();
|
||||
} else {
|
||||
// Refresh state
|
||||
group.membershipLevel = GroupMembershipLevel.PENDING;
|
||||
this.setState(() {});
|
||||
|
||||
if (this.widget.onUpdated != null) this.widget.onUpdated();
|
||||
if (this.widget.onUpdated != null) this.widget.onUpdated!();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -13,9 +13,9 @@ class IconButtonWithBadge extends StatelessWidget {
|
||||
final bool active;
|
||||
|
||||
const IconButtonWithBadge({
|
||||
Key key,
|
||||
@required this.icon,
|
||||
@required this.onPressed,
|
||||
Key? key,
|
||||
required this.icon,
|
||||
required this.onPressed,
|
||||
this.number = 0,
|
||||
this.active = false,
|
||||
}) : assert(icon != null),
|
||||
|
@ -69,7 +69,7 @@ class _InitializeWidgetState extends SafeState<InitializeWidget> {
|
||||
await ServerConfigurationHelper.ensureLoaded();
|
||||
|
||||
if (!isWeb &&
|
||||
ServerConfigurationHelper.config.minSupportedMobileVersion >
|
||||
ServerConfigurationHelper.config!.minSupportedMobileVersion >
|
||||
VersionHelper.version) await showDeprecationDialog(context);
|
||||
|
||||
if (!AccountHelper.isUserIDLoaded) {
|
||||
@ -91,7 +91,7 @@ class _InitializeWidgetState extends SafeState<InitializeWidget> {
|
||||
print("Attempting WebSocket connection...");
|
||||
|
||||
if (config().additionalLoading != null)
|
||||
await config().additionalLoading();
|
||||
await config().additionalLoading!();
|
||||
|
||||
await WebSocketHelper.connect();
|
||||
|
||||
@ -113,7 +113,7 @@ class _InitializeWidgetState extends SafeState<InitializeWidget> {
|
||||
if (_error || !WebSocketHelper.isConnected()) return _buildNonReadyWidget();
|
||||
|
||||
if (config().mainRouteBuilder != null)
|
||||
return config().mainRouteBuilder(context, mainControllerKey);
|
||||
return config().mainRouteBuilder!(context, mainControllerKey);
|
||||
|
||||
return isTablet(context)
|
||||
? TabletRoute(key: mainControllerKey)
|
||||
@ -149,9 +149,9 @@ class _InitializeWidgetState extends SafeState<InitializeWidget> {
|
||||
),
|
||||
!isWeb
|
||||
? Text(tr("Version %version% - Build %build%", args: {
|
||||
"version": VersionHelper.info.version.toString(),
|
||||
"build": VersionHelper.info.buildNumber.toString()
|
||||
}))
|
||||
"version": VersionHelper.info!.version.toString(),
|
||||
"build": VersionHelper.info!.buildNumber.toString()
|
||||
})!)
|
||||
: Container(),
|
||||
Spacer(flex: 1),
|
||||
],
|
||||
@ -171,13 +171,13 @@ class _InitializeWidgetState extends SafeState<InitializeWidget> {
|
||||
children: <Widget>[
|
||||
Icon(Icons.error, color: Colors.white),
|
||||
SizedBox(height: 30),
|
||||
Text(tr("Could not connect to server!")),
|
||||
Text(tr("Could not connect to server!")!),
|
||||
SizedBox(
|
||||
height: 30,
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: () => _tryConnect(),
|
||||
child: Text(tr("Try again")),
|
||||
child: Text(tr("Try again")!),
|
||||
),
|
||||
],
|
||||
);
|
||||
@ -186,6 +186,6 @@ class _InitializeWidgetState extends SafeState<InitializeWidget> {
|
||||
void _popToMainRoute() {
|
||||
// Pop until we reach main route
|
||||
Navigator.of(context).popUntil((settings) =>
|
||||
ModalRoute.of(context).isCurrent || !ModalRoute.of(context).isActive);
|
||||
ModalRoute.of(context)!.isCurrent || !ModalRoute.of(context)!.isActive);
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user