mirror of
				https://gitlab.com/comunic/comunicmobile
				synced 2025-11-04 04:04:18 +00:00 
			
		
		
		
	Start to fix null safety migration errors
This commit is contained in:
		@@ -5,6 +5,6 @@ import 'package:comunic/helpers/account_helper.dart';
 | 
			
		||||
/// @author Pierre HUBERT
 | 
			
		||||
 | 
			
		||||
/// Get the ID of the current user
 | 
			
		||||
int userID() {
 | 
			
		||||
int? userID() {
 | 
			
		||||
  return AccountHelper.getCurrentUserID();
 | 
			
		||||
}
 | 
			
		||||
@@ -3,8 +3,8 @@
 | 
			
		||||
/// @author Pierre Hubert
 | 
			
		||||
 | 
			
		||||
/// Casting helper
 | 
			
		||||
T cast<T>(dynamic val) => val is T ? val : null;
 | 
			
		||||
T? cast<T>(dynamic val) => val is T ? val : null;
 | 
			
		||||
 | 
			
		||||
/// Turn null and "null" into ""
 | 
			
		||||
String nullToEmpty(String input) =>
 | 
			
		||||
String nullToEmpty(String? input) =>
 | 
			
		||||
    input == null || input == "null" ? "" : input;
 | 
			
		||||
 
 | 
			
		||||
@@ -6,33 +6,33 @@ import 'package:flutter/material.dart';
 | 
			
		||||
 | 
			
		||||
/// This callback return null if the text has to be left as is or a TextSpan
 | 
			
		||||
/// if it has been sub parsed...
 | 
			
		||||
typedef ParseCallBack = List<InlineSpan> Function(TextStyle, String);
 | 
			
		||||
typedef ParseCallBack = List<InlineSpan> Function(TextStyle, String?);
 | 
			
		||||
 | 
			
		||||
class BBCodeParsedWidget extends StatelessWidget {
 | 
			
		||||
  final _Element _content;
 | 
			
		||||
  final ParseCallBack parseCallback;
 | 
			
		||||
  final _Element? _content;
 | 
			
		||||
  final ParseCallBack? parseCallback;
 | 
			
		||||
 | 
			
		||||
  BBCodeParsedWidget({@required String text, this.parseCallback})
 | 
			
		||||
  BBCodeParsedWidget({required String text, this.parseCallback})
 | 
			
		||||
      : assert(text != null),
 | 
			
		||||
        _content = _parse(text);
 | 
			
		||||
 | 
			
		||||
  _printRecur(_Element el, {int pos = 0}) {
 | 
			
		||||
    String str;
 | 
			
		||||
    str = "".padLeft(pos, "*");
 | 
			
		||||
    if (el.text != null) print(str + el.text);
 | 
			
		||||
    el.children.forEach((f) => _printRecur(f, pos: pos + 1));
 | 
			
		||||
    if (el.text != null) print(str + el.text!);
 | 
			
		||||
    el.children.forEach((f) => _printRecur(f!, pos: pos + 1));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    _printRecur(_content);
 | 
			
		||||
    _printRecur(_content!);
 | 
			
		||||
    return RichText(
 | 
			
		||||
      text: _content.toTextSpan(context, parseCallback),
 | 
			
		||||
      text: _content!.toTextSpan(context, parseCallback),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /// Initialize parsing
 | 
			
		||||
  static _Element _parse(String text) {
 | 
			
		||||
  static _Element? _parse(String text) {
 | 
			
		||||
    try {
 | 
			
		||||
      return _parseRecur(
 | 
			
		||||
        text: text,
 | 
			
		||||
@@ -48,19 +48,19 @@ class BBCodeParsedWidget extends StatelessWidget {
 | 
			
		||||
 | 
			
		||||
  /// Recursive parsing
 | 
			
		||||
  static _ElementRecur _parseRecur({
 | 
			
		||||
    @required String text,
 | 
			
		||||
    @required _ElementStyle style,
 | 
			
		||||
    @required int pos,
 | 
			
		||||
    String parentTag,
 | 
			
		||||
    required String text,
 | 
			
		||||
    required _ElementStyle style,
 | 
			
		||||
    required int pos,
 | 
			
		||||
    String? parentTag,
 | 
			
		||||
  }) {
 | 
			
		||||
    _Element el = _Element(style: style.clone());
 | 
			
		||||
 | 
			
		||||
    int lastBeginPos = pos;
 | 
			
		||||
    int? lastBeginPos = pos;
 | 
			
		||||
    int childNumber = 0;
 | 
			
		||||
    bool stop = false;
 | 
			
		||||
    while (!stop && pos < text.length) {
 | 
			
		||||
    while (!stop && pos! < text.length) {
 | 
			
		||||
      //Go to next stop
 | 
			
		||||
      while (!stop && pos < text.length) {
 | 
			
		||||
      while (!stop && pos! < text.length) {
 | 
			
		||||
        if (text[pos] == '[') break;
 | 
			
		||||
        pos++;
 | 
			
		||||
      }
 | 
			
		||||
@@ -68,7 +68,7 @@ class BBCodeParsedWidget extends StatelessWidget {
 | 
			
		||||
      //Check for text with default style to apply
 | 
			
		||||
      if (lastBeginPos != pos)
 | 
			
		||||
        el.children.add(_Element(
 | 
			
		||||
            style: style.clone(), text: text.substring(lastBeginPos, pos)));
 | 
			
		||||
            style: style.clone(), text: text.substring(lastBeginPos!, pos)));
 | 
			
		||||
 | 
			
		||||
      //Check if the [ tag is alone
 | 
			
		||||
      if (pos == text.length)
 | 
			
		||||
@@ -89,7 +89,7 @@ class BBCodeParsedWidget extends StatelessWidget {
 | 
			
		||||
        // Prepare tag detection
 | 
			
		||||
        final closeBrace = text.indexOf("]", pos);
 | 
			
		||||
        String tag = text.substring(pos + 1, closeBrace);
 | 
			
		||||
        String arg;
 | 
			
		||||
        String? arg;
 | 
			
		||||
        final newStyle = style.clone();
 | 
			
		||||
 | 
			
		||||
        //Check for argument
 | 
			
		||||
@@ -126,7 +126,7 @@ class BBCodeParsedWidget extends StatelessWidget {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /// Pre-parse tag
 | 
			
		||||
  static void _preParseTag(String tag, _ElementStyle style, [String arg]) {
 | 
			
		||||
  static void _preParseTag(String tag, _ElementStyle style, [String? arg]) {
 | 
			
		||||
    switch (tag) {
 | 
			
		||||
      // Bold
 | 
			
		||||
      case "b":
 | 
			
		||||
@@ -153,9 +153,9 @@ class BBCodeParsedWidget extends StatelessWidget {
 | 
			
		||||
        assert(arg != null);
 | 
			
		||||
        style.color = Color.fromARGB(
 | 
			
		||||
          255,
 | 
			
		||||
          int.tryParse(arg.substring(1, 3), radix: 16),
 | 
			
		||||
          int.tryParse(arg.substring(3, 5), radix: 16),
 | 
			
		||||
          int.tryParse(arg.substring(5, 7), radix: 16),
 | 
			
		||||
          int.tryParse(arg!.substring(1, 3), radix: 16)!,
 | 
			
		||||
          int.tryParse(arg.substring(3, 5), radix: 16)!,
 | 
			
		||||
          int.tryParse(arg.substring(5, 7), radix: 16)!,
 | 
			
		||||
        );
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
@@ -167,18 +167,18 @@ class BBCodeParsedWidget extends StatelessWidget {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /// Post-parse tag
 | 
			
		||||
  static void _postParseTag(String tag, _Element el,
 | 
			
		||||
      {String arg, String parentTag, int childNumber}) {
 | 
			
		||||
  static void _postParseTag(String tag, _Element? el,
 | 
			
		||||
      {String? arg, String? parentTag, int? childNumber}) {
 | 
			
		||||
    // List container
 | 
			
		||||
    if (tag == "ul" || tag == "ol")
 | 
			
		||||
      el.children.insert(0, _Element(style: el.style, text: "\n"));
 | 
			
		||||
      el!.children.insert(0, _Element(style: el.style, text: "\n"));
 | 
			
		||||
 | 
			
		||||
    // List children
 | 
			
		||||
    if (tag == "li") {
 | 
			
		||||
      el.children.add(_Element(style: el.style, text: "\n"));
 | 
			
		||||
      el!.children.add(_Element(style: el.style, text: "\n"));
 | 
			
		||||
      if (parentTag == "ol")
 | 
			
		||||
        el.children.insert(
 | 
			
		||||
            0, _Element(style: el.style, text: " ${childNumber + 1}. "));
 | 
			
		||||
            0, _Element(style: el.style, text: " ${childNumber! + 1}. "));
 | 
			
		||||
      else
 | 
			
		||||
        el.children.insert(0, _Element(style: el.style, text: " \u2022 "));
 | 
			
		||||
    }
 | 
			
		||||
@@ -187,20 +187,20 @@ class BBCodeParsedWidget extends StatelessWidget {
 | 
			
		||||
 | 
			
		||||
/// An element's style
 | 
			
		||||
class _ElementStyle {
 | 
			
		||||
  TextDecoration decoration;
 | 
			
		||||
  FontWeight fontWeight;
 | 
			
		||||
  FontStyle fontStyle;
 | 
			
		||||
  Color color;
 | 
			
		||||
  TextDecoration? decoration;
 | 
			
		||||
  FontWeight? fontWeight;
 | 
			
		||||
  FontStyle? fontStyle;
 | 
			
		||||
  Color? color;
 | 
			
		||||
 | 
			
		||||
  /// Generate an empty style
 | 
			
		||||
  _ElementStyle.empty();
 | 
			
		||||
 | 
			
		||||
  /// Construct an instance of this element
 | 
			
		||||
  _ElementStyle(
 | 
			
		||||
      {@required this.decoration,
 | 
			
		||||
      @required this.fontWeight,
 | 
			
		||||
      @required this.fontStyle,
 | 
			
		||||
      @required this.color});
 | 
			
		||||
      {required this.decoration,
 | 
			
		||||
      required this.fontWeight,
 | 
			
		||||
      required this.fontStyle,
 | 
			
		||||
      required this.color});
 | 
			
		||||
 | 
			
		||||
  /// Clone this style
 | 
			
		||||
  _ElementStyle clone() {
 | 
			
		||||
@@ -213,7 +213,7 @@ class _ElementStyle {
 | 
			
		||||
 | 
			
		||||
  /// Generate corresponding TextStyle
 | 
			
		||||
  TextStyle toTextStyle(BuildContext context) {
 | 
			
		||||
    return Theme.of(context).textTheme.bodyText2.copyWith(
 | 
			
		||||
    return Theme.of(context).textTheme.bodyText2!.copyWith(
 | 
			
		||||
        decoration: decoration,
 | 
			
		||||
        fontWeight: fontWeight,
 | 
			
		||||
        fontStyle: fontStyle,
 | 
			
		||||
@@ -224,14 +224,14 @@ class _ElementStyle {
 | 
			
		||||
/// An element
 | 
			
		||||
class _Element {
 | 
			
		||||
  /// Note : if text is not null, children must be empty !!!
 | 
			
		||||
  String text;
 | 
			
		||||
  String? text;
 | 
			
		||||
  final _ElementStyle style;
 | 
			
		||||
  final List<_Element> children = [];
 | 
			
		||||
  final List<_Element?> children = [];
 | 
			
		||||
 | 
			
		||||
  _Element({@required this.style, this.text});
 | 
			
		||||
  _Element({required this.style, this.text});
 | 
			
		||||
 | 
			
		||||
  /// Turn this element into a TextSpan
 | 
			
		||||
  TextSpan toTextSpan(BuildContext context, ParseCallBack parseCallback) {
 | 
			
		||||
  TextSpan toTextSpan(BuildContext context, ParseCallBack? parseCallback) {
 | 
			
		||||
    assert(text == null || children.length == 0);
 | 
			
		||||
 | 
			
		||||
    final generatedStyle = this.style.toTextStyle(context);
 | 
			
		||||
@@ -250,14 +250,14 @@ class _Element {
 | 
			
		||||
      text: text,
 | 
			
		||||
      style: generatedStyle,
 | 
			
		||||
      children:
 | 
			
		||||
          children.map((f) => f.toTextSpan(context, parseCallback)).toList(),
 | 
			
		||||
          children.map((f) => f!.toTextSpan(context, parseCallback)).toList(),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class _ElementRecur {
 | 
			
		||||
  final _Element el;
 | 
			
		||||
  final _Element? el;
 | 
			
		||||
  final int finalPos;
 | 
			
		||||
 | 
			
		||||
  const _ElementRecur({this.el, this.finalPos});
 | 
			
		||||
  const _ElementRecur({this.el, required this.finalPos});
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -10,5 +10,5 @@ import 'intl_utils.dart';
 | 
			
		||||
 | 
			
		||||
void copyToClipboard(BuildContext context, String content) {
 | 
			
		||||
  FlutterClipboard.copy(content);
 | 
			
		||||
  snack(context, tr("'%1%' copied to clipboard!", args: {"1": content}));
 | 
			
		||||
  snack(context, tr("'%1%' copied to clipboard!", args: {"1": content})!);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,7 @@ import 'dart:ui';
 | 
			
		||||
///
 | 
			
		||||
/// @author Pierre Hubert
 | 
			
		||||
 | 
			
		||||
String colorToHex(Color color) {
 | 
			
		||||
String colorToHex(Color? color) {
 | 
			
		||||
  if (color == null) return "";
 | 
			
		||||
 | 
			
		||||
  return (color.red.toRadixString(16).padLeft(2, '0') +
 | 
			
		||||
 
 | 
			
		||||
@@ -9,17 +9,17 @@ import 'package:flutter/material.dart';
 | 
			
		||||
/// @author Pierre HUBERT
 | 
			
		||||
 | 
			
		||||
/// Open a private conversation with a given [userID]
 | 
			
		||||
Future<bool> openPrivateConversation(BuildContext context, int userID) async {
 | 
			
		||||
Future<bool> openPrivateConversation(BuildContext context, int? userID) async {
 | 
			
		||||
  try {
 | 
			
		||||
    final convID = await ConversationsHelper().getPrivate(userID);
 | 
			
		||||
 | 
			
		||||
    // Open the conversation
 | 
			
		||||
    MainController.of(context).openConversationById(convID);
 | 
			
		||||
    MainController.of(context)!.openConversationById(convID);
 | 
			
		||||
 | 
			
		||||
    return true;
 | 
			
		||||
  } catch (e, s) {
 | 
			
		||||
    print("Failed to find private conversation! $e => $s");
 | 
			
		||||
    showSimpleSnack(context, tr("Could not find a private conversation!"));
 | 
			
		||||
    showSimpleSnack(context, tr("Could not find a private conversation!")!);
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -10,7 +10,7 @@ int time() {
 | 
			
		||||
  return (DateTime.now().millisecondsSinceEpoch / 1000).floor();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
String diffTimeToStr(int amount) {
 | 
			
		||||
String? diffTimeToStr(int amount) {
 | 
			
		||||
  if (amount < 0) amount = 0;
 | 
			
		||||
 | 
			
		||||
  // Seconds
 | 
			
		||||
@@ -45,7 +45,7 @@ String diffTimeToStr(int amount) {
 | 
			
		||||
      : tr("%years% years", args: {"years": years.toString()});
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
String diffTimeFromNowToStr(int date) {
 | 
			
		||||
String? diffTimeFromNowToStr(int date) {
 | 
			
		||||
  return diffTimeToStr(time() - date);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -59,7 +59,7 @@ String formatDuration(Duration d) =>
 | 
			
		||||
    "${d.inMinutes < 10 ? "0" + d.inMinutes.toString() : d.inMinutes.toString()}:${d.inSeconds % 60 < 10 ? "0" + (d.inSeconds % 60).toString() : (d.inSeconds % 60).toString()}";
 | 
			
		||||
 | 
			
		||||
/// Compare two [DateTime] on their date
 | 
			
		||||
bool isSameDate(DateTime one, DateTime other) {
 | 
			
		||||
bool isSameDate(DateTime one, DateTime? other) {
 | 
			
		||||
  if (other == null) return false;
 | 
			
		||||
  return one.year == other.year &&
 | 
			
		||||
      one.month == other.month &&
 | 
			
		||||
 
 | 
			
		||||
@@ -16,8 +16,8 @@ import '../ui/dialogs/pick_file_dialog.dart';
 | 
			
		||||
/// Ask the user to choose an image, either from the gallery or using the camera
 | 
			
		||||
///
 | 
			
		||||
/// Throws an exception null in case of failure
 | 
			
		||||
Future<BytesFile> pickImage(BuildContext context,
 | 
			
		||||
    {CropAspectRatio aspectRatio}) async {
 | 
			
		||||
Future<BytesFile?> pickImage(BuildContext context,
 | 
			
		||||
    {CropAspectRatio? aspectRatio}) async {
 | 
			
		||||
  return await showPickFileDialog(
 | 
			
		||||
    context: context,
 | 
			
		||||
    allowedMimeTypes: ["image/png", "image/jpeg", "image/gif"],
 | 
			
		||||
 
 | 
			
		||||
@@ -10,11 +10,10 @@ import 'package:random_string/random_string.dart';
 | 
			
		||||
///
 | 
			
		||||
/// @author Pierre Hubert
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// Based on https://stackoverflow.com/a/63215502/3781411
 | 
			
		||||
Future<Uint8List> svgToPng(BuildContext context, String svgString,
 | 
			
		||||
    {int svgWidth, int svgHeight}) async {
 | 
			
		||||
  DrawableRoot svgDrawableRoot = await svg.fromSvgString(svgString, null);
 | 
			
		||||
    {int? svgWidth, int? svgHeight}) async {
 | 
			
		||||
  DrawableRoot svgDrawableRoot = await svg.fromSvgString(svgString, "svg");
 | 
			
		||||
 | 
			
		||||
  // to have a nice rendering it is important to have the exact original height and width,
 | 
			
		||||
  // the easier way to retrieve it is directly from the svg string
 | 
			
		||||
@@ -40,7 +39,7 @@ Future<Uint8List> svgToPng(BuildContext context, String svgString,
 | 
			
		||||
  // Convert to ui.Image. toImage() takes width and height as parameters
 | 
			
		||||
  // you need to find the best size to suit your needs and take into account the screen DPI
 | 
			
		||||
  final image = await picture.toImage(width.toInt(), height.toInt());
 | 
			
		||||
  ByteData bytes = await image.toByteData(format: ImageByteFormat.png);
 | 
			
		||||
  ByteData bytes = (await image.toByteData(format: ImageByteFormat.png))!;
 | 
			
		||||
 | 
			
		||||
  return bytes.buffer.asUint8List();
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -12,7 +12,7 @@ bool legacyValidatePassword(String s) => s.length > 3;
 | 
			
		||||
bool validateEmail(String value) {
 | 
			
		||||
  Pattern pattern =
 | 
			
		||||
      r'^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$';
 | 
			
		||||
  RegExp regex = new RegExp(pattern);
 | 
			
		||||
  RegExp regex = new RegExp(pattern as String);
 | 
			
		||||
  return regex.hasMatch(value);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -8,8 +8,8 @@ import 'package:intl/intl_standalone.dart';
 | 
			
		||||
///
 | 
			
		||||
/// @author Pierre HUBERT
 | 
			
		||||
 | 
			
		||||
String _currLang;
 | 
			
		||||
Map<String, Map<String, String>> translations;
 | 
			
		||||
String? _currLang;
 | 
			
		||||
late Map<String, Map<String, String>> translations;
 | 
			
		||||
 | 
			
		||||
/// Initialize translations system
 | 
			
		||||
///
 | 
			
		||||
@@ -35,22 +35,22 @@ Future<void> initTranslations() async {
 | 
			
		||||
///
 | 
			
		||||
/// Then apply the list of [args] to the string, each argument name is
 | 
			
		||||
/// surrounded by '%'
 | 
			
		||||
String tr(String string, {Map<String, String> args}) {
 | 
			
		||||
String? tr(String? string, {Map<String, String?>? args}) {
 | 
			
		||||
  // Check if a translation is available
 | 
			
		||||
  if (_currLang != null &&
 | 
			
		||||
      translations.containsKey(_currLang) &&
 | 
			
		||||
      translations[_currLang].containsKey(string))
 | 
			
		||||
    string = translations[_currLang][string];
 | 
			
		||||
      translations[_currLang!]!.containsKey(string))
 | 
			
		||||
    string = translations[_currLang!]![string!];
 | 
			
		||||
 | 
			
		||||
  //Apply arguments
 | 
			
		||||
  if (args != null)
 | 
			
		||||
    args.forEach((key, value) => string = string.replaceAll("%$key%", value));
 | 
			
		||||
    args.forEach((key, value) => string = string!.replaceAll("%$key%", value!));
 | 
			
		||||
 | 
			
		||||
  return string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Get current lang, in format aa_BB
 | 
			
		||||
String/*!*/ get lang => _currLang != null ? _currLang : "en_US";
 | 
			
		||||
String get lang => _currLang != null ? _currLang! : "en_US";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/// Get short lang format, in format aa
 | 
			
		||||
 
 | 
			
		||||
@@ -11,20 +11,20 @@ import 'package:flutter/material.dart';
 | 
			
		||||
 | 
			
		||||
/// Pop a page
 | 
			
		||||
void popPage(BuildContext context) {
 | 
			
		||||
  MainController.of(context).popPage();
 | 
			
		||||
  MainController.of(context)!.popPage();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Open the page of a user
 | 
			
		||||
void openUserPage({@required int userID, @required BuildContext context}) {
 | 
			
		||||
void openUserPage({required int userID, required BuildContext context}) {
 | 
			
		||||
  assert(userID != null);
 | 
			
		||||
  assert(context != null);
 | 
			
		||||
 | 
			
		||||
  MainController.of(context).openUserPage(userID);
 | 
			
		||||
  MainController.of(context)!.openUserPage(userID);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Open a post in full screen
 | 
			
		||||
void openPostFullScreen(int postID, BuildContext context) {
 | 
			
		||||
  MainController.of(context).push(SinglePostRoute(postID: postID));
 | 
			
		||||
  MainController.of(context)!.push(SinglePostRoute(postID: postID));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Open a virtual directory
 | 
			
		||||
@@ -36,22 +36,22 @@ void openVirtualDirectory(BuildContext context, String directory) async {
 | 
			
		||||
 | 
			
		||||
    switch (result.type) {
 | 
			
		||||
      case VirtualDirectoryType.USER:
 | 
			
		||||
        openUserPage(context: context, userID: result.id);
 | 
			
		||||
        openUserPage(context: context, userID: result.id!);
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case VirtualDirectoryType.GROUP:
 | 
			
		||||
        MainController.of(context).openGroup(result.id);
 | 
			
		||||
        MainController.of(context)!.openGroup(result.id!);
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      case VirtualDirectoryType.NONE:
 | 
			
		||||
        await showDialog(
 | 
			
		||||
            context: context,
 | 
			
		||||
            builder: (c) => AlertDialog(
 | 
			
		||||
                  title: Text(tr("Error")),
 | 
			
		||||
                  content: Text(tr("Could not find related resource!")),
 | 
			
		||||
                  title: Text(tr("Error")!),
 | 
			
		||||
                  content: Text(tr("Could not find related resource!")!),
 | 
			
		||||
                  actions: <Widget>[
 | 
			
		||||
                    MaterialButton(
 | 
			
		||||
                      child: Text(tr("OK")),
 | 
			
		||||
                      child: Text(tr("OK")!),
 | 
			
		||||
                      onPressed: () => Navigator.of(c).pop(),
 | 
			
		||||
                    )
 | 
			
		||||
                  ],
 | 
			
		||||
@@ -61,6 +61,6 @@ void openVirtualDirectory(BuildContext context, String directory) async {
 | 
			
		||||
  } catch (e, stack) {
 | 
			
		||||
    print(e);
 | 
			
		||||
    print(stack);
 | 
			
		||||
    showSimpleSnack(context, tr("Could not search virtual directory!"));
 | 
			
		||||
    showSimpleSnack(context, tr("Could not search virtual directory!")!);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -23,7 +23,7 @@ Widget buildCenteredProgressBar() {
 | 
			
		||||
/// Build and return a full loading page
 | 
			
		||||
Widget buildLoadingPage({
 | 
			
		||||
  bool showAppBar = false,
 | 
			
		||||
  String routeTitle,
 | 
			
		||||
  String? routeTitle,
 | 
			
		||||
}) {
 | 
			
		||||
  return Scaffold(
 | 
			
		||||
    appBar: showAppBar
 | 
			
		||||
@@ -36,8 +36,8 @@ Widget buildLoadingPage({
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Build and return an error card
 | 
			
		||||
Widget buildErrorCard(String message,
 | 
			
		||||
    {List<Widget> actions, bool hide = false}) {
 | 
			
		||||
Widget buildErrorCard(String? message,
 | 
			
		||||
    {List<Widget>? actions, bool hide = false}) {
 | 
			
		||||
  if (hide) return Container();
 | 
			
		||||
 | 
			
		||||
  return Theme(
 | 
			
		||||
@@ -59,7 +59,7 @@ Widget buildErrorCard(String message,
 | 
			
		||||
            ),
 | 
			
		||||
            Flexible(
 | 
			
		||||
              child: Text(
 | 
			
		||||
                message,
 | 
			
		||||
                message!,
 | 
			
		||||
                maxLines: null,
 | 
			
		||||
              ),
 | 
			
		||||
            ),
 | 
			
		||||
@@ -74,9 +74,9 @@ Widget buildErrorCard(String message,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Show an image with a given [url] in full screen
 | 
			
		||||
void showImageFullScreen(BuildContext context, String url) {
 | 
			
		||||
void showImageFullScreen(BuildContext context, String? url) {
 | 
			
		||||
  Navigator.of(context).push(MaterialPageRoute(builder: (c) {
 | 
			
		||||
    return FullScreenImageRoute(url);
 | 
			
		||||
    return FullScreenImageRoute(url!);
 | 
			
		||||
  }));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -92,12 +92,12 @@ void snack(BuildContext context, String message) {
 | 
			
		||||
/// Show an alert dialog to ask the user to enter a string
 | 
			
		||||
///
 | 
			
		||||
/// Returns entered string if the dialog is confirmed, null else
 | 
			
		||||
Future<String> askUserString({
 | 
			
		||||
  @required BuildContext context,
 | 
			
		||||
  @required String title,
 | 
			
		||||
  @required String message,
 | 
			
		||||
  @required String defaultValue,
 | 
			
		||||
  @required String hint,
 | 
			
		||||
Future<String?> askUserString({
 | 
			
		||||
  required BuildContext context,
 | 
			
		||||
  required String title,
 | 
			
		||||
  required String message,
 | 
			
		||||
  required String defaultValue,
 | 
			
		||||
  required String hint,
 | 
			
		||||
  int maxLength = 200,
 | 
			
		||||
  int minLength = 1,
 | 
			
		||||
}) async {
 | 
			
		||||
@@ -135,13 +135,13 @@ class _InputTextDialog extends StatefulWidget {
 | 
			
		||||
  final String hint;
 | 
			
		||||
 | 
			
		||||
  const _InputTextDialog({
 | 
			
		||||
    Key key,
 | 
			
		||||
    @required this.title,
 | 
			
		||||
    @required this.message,
 | 
			
		||||
    @required this.controller,
 | 
			
		||||
    @required this.maxLength,
 | 
			
		||||
    @required this.minLength,
 | 
			
		||||
    @required this.hint,
 | 
			
		||||
    Key? key,
 | 
			
		||||
    required this.title,
 | 
			
		||||
    required this.message,
 | 
			
		||||
    required this.controller,
 | 
			
		||||
    required this.maxLength,
 | 
			
		||||
    required this.minLength,
 | 
			
		||||
    required this.hint,
 | 
			
		||||
  }) : super(key: key);
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
@@ -172,11 +172,11 @@ class __InputTextDialogState extends State<_InputTextDialog> {
 | 
			
		||||
        ),
 | 
			
		||||
        actions: <Widget>[
 | 
			
		||||
          TextButton(
 | 
			
		||||
            child: Text(tr("Cancel").toUpperCase()),
 | 
			
		||||
            child: Text(tr("Cancel")!.toUpperCase()),
 | 
			
		||||
            onPressed: () => Navigator.pop(c, false),
 | 
			
		||||
          ),
 | 
			
		||||
          TextButton(
 | 
			
		||||
            child: Text(tr("OK")),
 | 
			
		||||
            child: Text(tr("OK")!),
 | 
			
		||||
            onPressed: widget.controller.text.length >= widget.minLength
 | 
			
		||||
                ? () => Navigator.pop(c, true)
 | 
			
		||||
                : null,
 | 
			
		||||
@@ -189,9 +189,9 @@ class __InputTextDialogState extends State<_InputTextDialog> {
 | 
			
		||||
///
 | 
			
		||||
/// Return value of this function is never null
 | 
			
		||||
Future<bool> showConfirmDialog({
 | 
			
		||||
  @required BuildContext context,
 | 
			
		||||
  String title,
 | 
			
		||||
  @required String message,
 | 
			
		||||
  required BuildContext context,
 | 
			
		||||
  String? title,
 | 
			
		||||
  required String? message,
 | 
			
		||||
}) async {
 | 
			
		||||
  if (title == null) title = tr("Confirm operation");
 | 
			
		||||
 | 
			
		||||
@@ -202,17 +202,17 @@ Future<bool> showConfirmDialog({
 | 
			
		||||
  final result = await showDialog<bool>(
 | 
			
		||||
      context: ctx,
 | 
			
		||||
      builder: (c) => AlertDialog(
 | 
			
		||||
            title: Text(title),
 | 
			
		||||
            content: Text(message),
 | 
			
		||||
            title: Text(title!),
 | 
			
		||||
            content: Text(message!),
 | 
			
		||||
            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),
 | 
			
		||||
                ),
 | 
			
		||||
              ),
 | 
			
		||||
@@ -224,15 +224,15 @@ Future<bool> showConfirmDialog({
 | 
			
		||||
 | 
			
		||||
/// Show a simple alert dialog
 | 
			
		||||
Future<void> showAlert({
 | 
			
		||||
  @required BuildContext context,
 | 
			
		||||
  @required String message,
 | 
			
		||||
  String title,
 | 
			
		||||
  required BuildContext context,
 | 
			
		||||
  required String? message,
 | 
			
		||||
  String? title,
 | 
			
		||||
}) async =>
 | 
			
		||||
    await showDialog(
 | 
			
		||||
        context: context,
 | 
			
		||||
        builder: (c) => AlertDialog(
 | 
			
		||||
              title: title == null ? null : Text(title),
 | 
			
		||||
              content: Text(message),
 | 
			
		||||
              content: Text(message!),
 | 
			
		||||
              actions: [CancelDialogButton()],
 | 
			
		||||
            ));
 | 
			
		||||
 | 
			
		||||
@@ -240,28 +240,28 @@ Future<void> showAlert({
 | 
			
		||||
/// text has already been entered by the user
 | 
			
		||||
Widget smartInputCounterWidgetBuilder(
 | 
			
		||||
  BuildContext context, {
 | 
			
		||||
  @required int currentLength,
 | 
			
		||||
  @required int maxLength,
 | 
			
		||||
  @required bool isFocused,
 | 
			
		||||
  required int currentLength,
 | 
			
		||||
  required int? maxLength,
 | 
			
		||||
  required bool isFocused,
 | 
			
		||||
}) =>
 | 
			
		||||
    currentLength > 0 ? Text("$currentLength/$maxLength") : Container();
 | 
			
		||||
 | 
			
		||||
/// Parse an HTML String to decode special characters
 | 
			
		||||
String htmlDecodeCharacters(String input) {
 | 
			
		||||
String htmlDecodeCharacters(String? input) {
 | 
			
		||||
  if (input == null || input == "") return "";
 | 
			
		||||
 | 
			
		||||
  return parse(input).documentElement.text;
 | 
			
		||||
  return parse(input).documentElement!.text;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const darkAccentColor = Colors.white70;
 | 
			
		||||
const darkerAccentColor = Colors.white30;
 | 
			
		||||
 | 
			
		||||
/// Check out whether dark theme is enabled or not
 | 
			
		||||
bool darkTheme() => preferences().getBool(PreferencesKeyList.ENABLE_DARK_THEME);
 | 
			
		||||
bool darkTheme() => preferences()!.getBool(PreferencesKeyList.ENABLE_DARK_THEME);
 | 
			
		||||
 | 
			
		||||
/// Check out whether we use tablet mode or not
 | 
			
		||||
bool isTablet(BuildContext context) =>
 | 
			
		||||
    !preferences().getBool(PreferencesKeyList.FORCE_MOBILE_MODE) &&
 | 
			
		||||
    !preferences()!.getBool(PreferencesKeyList.FORCE_MOBILE_MODE) &&
 | 
			
		||||
    MediaQuery.of(context).size.width >= 1024;
 | 
			
		||||
 | 
			
		||||
/// Show about Comunic dialog
 | 
			
		||||
@@ -272,7 +272,7 @@ void showAboutAppDialog(BuildContext context) {
 | 
			
		||||
      children: <Widget>[
 | 
			
		||||
        Text(
 | 
			
		||||
          tr(config().appQuickDescription) ??
 | 
			
		||||
              tr("Comunic is a free and OpenSource social network that respect your privacy."),
 | 
			
		||||
              tr("Comunic is a free and OpenSource social network that respect your privacy.")!,
 | 
			
		||||
          textAlign: TextAlign.center,
 | 
			
		||||
        ),
 | 
			
		||||
        SizedBox(
 | 
			
		||||
@@ -287,7 +287,7 @@ void showAboutAppDialog(BuildContext context) {
 | 
			
		||||
 | 
			
		||||
/// Apply new theme settings
 | 
			
		||||
void applyNewThemeSettings(BuildContext context) =>
 | 
			
		||||
    context.findAncestorStateOfType<ComunicApplicationState>().refresh();
 | 
			
		||||
    context.findAncestorStateOfType<ComunicApplicationState>()!.refresh();
 | 
			
		||||
 | 
			
		||||
/// Parse emojies
 | 
			
		||||
String parseEmojies(String input) => EmojiParser().emojify(input);
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,6 @@ import 'dart:io';
 | 
			
		||||
 | 
			
		||||
import 'package:comunic/models/api_request.dart';
 | 
			
		||||
import 'package:comunic/utils/log_utils.dart';
 | 
			
		||||
import 'package:flutter/material.dart';
 | 
			
		||||
import 'package:video_thumbnail/video_thumbnail.dart';
 | 
			
		||||
 | 
			
		||||
import 'files_utils.dart';
 | 
			
		||||
@@ -12,16 +11,16 @@ import 'files_utils.dart';
 | 
			
		||||
/// @author Pierre Hubert
 | 
			
		||||
 | 
			
		||||
/// Generate a thumbnail for a video. In case of failure, return null
 | 
			
		||||
Future<BytesFile> generateVideoThumbnail({
 | 
			
		||||
  @required BytesFile videoFile,
 | 
			
		||||
  int maxWidth,
 | 
			
		||||
Future<BytesFile?> generateVideoThumbnail({
 | 
			
		||||
  required BytesFile videoFile,
 | 
			
		||||
  required int maxWidth,
 | 
			
		||||
}) async {
 | 
			
		||||
  File file;
 | 
			
		||||
  File? file;
 | 
			
		||||
 | 
			
		||||
  try {
 | 
			
		||||
    file = await generateTemporaryFile();
 | 
			
		||||
 | 
			
		||||
    await file.writeAsBytes(videoFile.bytes);
 | 
			
		||||
    await file.writeAsBytes(videoFile.bytes!);
 | 
			
		||||
 | 
			
		||||
    return BytesFile(
 | 
			
		||||
      "thumb.png",
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user