import 'dart:async'; import 'dart:io'; import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:intl/intl.dart'; import 'package:jiffy/jiffy.dart'; import 'package:path_provider/path_provider.dart'; import 'package:timezone/timezone.dart' as tz; import 'package:tp5/providers/airports.dart'; Jiffy jiffyfromddmmmyyhhmm(String str) => Jiffy.parse( "${str.substring(0, 7)} ${str.substring(8, 10)}:${str.substring(10, 12)}", pattern: "ddMMMyy H:mm", isUtc: true); extension JiffyExtensions on Jiffy? { Jiffy? latest(Jiffy? val2) { if (this == null) return val2; if (val2 == null) return this; if (this!.isBefore(val2)) { return val2; } else { return this; } } Jiffy? earliset(Jiffy? val2) { if (this == null) return val2; if (val2 == null) return this; if (this!.isAfter(val2)) { return val2; } else { return this; } } Jiffy max(Jiffy val2) { if (this!.isBefore(val2)) { return val2; } else { return this!; } } Jiffy min(Jiffy val2) { if (this!.isAfter(val2)) { return val2; } else { return this!; } } Jiffy setTz({String? newtz, String? ap}) => Jiffy.parseFromDateTime(tz.TZDateTime.from( this!.dateTime, (ap != null) ? (tz.getLocation(Airports.find(ap)?.timezoneid ?? "UTC")) : (tz.getLocation(newtz ?? "UTC")))); } extension DurationLExtensions on List { Duration get sum => fold( Duration.zero, (p, e) => Duration(minutes: p.inMinutes + e.inMinutes)); } extension DurationExtensions on Duration { Duration max(Duration value) { return Duration( milliseconds: (inMilliseconds >= value.inMilliseconds) ? inMilliseconds : value.inMilliseconds); } Duration add(Duration value) { return Duration(milliseconds: inMilliseconds + value.inMilliseconds); } Duration subtract(Duration value) { return Duration(milliseconds: inMilliseconds - value.inMilliseconds); } Duration multiply(double value) { return Duration(milliseconds: (inMilliseconds * value).ceil()); } // Duration intersect(Jiffy start, Jiffy end, dynamic interval) { // if (interval is List && interval.length == 2) { // if ((interval[0].isSameOrBefore(start) && // interval[1].isSameOrBefore(start)) || // (interval[0].isSameOrAfter(end) && interval[1].isSameOrAfter(end))) { // return Duration.zero; // } else if (interval[0].isSameOrAfter(start) && // interval[1].isSameOrBefore(end)) { // return Duration( // minutes: // interval[1].diff(interval[0], unit: Unit.minute).toInt().abs()); // } else if (interval[0].isSameOrBefore(start) && // interval[1].isSameOrAfter(end)) { // return Duration( // minutes: start.diff(end, unit: Unit.minute).toInt().abs()); // } else if (interval[0].isSameOrBefore(start) && // interval[1].isSameOrBefore(end)) { // return Duration( // minutes: start.diff(interval[1], unit: Unit.minute).toInt().abs()); // } else if (interval[0].isSameOrAfter(start) && // interval[1].isSameOrAfter(end)) { // return Duration( // minutes: interval[0].diff(end, unit: Unit.minute).toInt().abs()); // } else { // return Duration.zero; // } // } else if (interval is List>) { // return Duration( // minutes: interval.map((e) => intersect(start, end, e).inMinutes).sum); // } // throw ("Unknown type in Duration intersect: ${interval.first.runtimeType}"); // // return Duration.zero; // } String get tohhmm => "${NumberFormat("00").format(inHours)}:${NumberFormat("00").format(inMinutes % 60)}"; } extension StringExtensions on String { String capitalize() { if (isEmpty) { return this; } else { return "${this[0].toUpperCase()}${substring(1).toLowerCase()}"; } } String capitalizeword() { return split(' ').map((word) => word.capitalize()).join(' '); } Jiffy? parseddmmyyyyhhmm() => length >= 15 ? Jiffy.parse( "${substring(6, 10)}-${substring(3, 5)}-${substring(0, 2)} ${substring(11, 13)}:${substring(13, 15)}", pattern: 'yyyy-MM-dd HH:mm', isUtc: true) : null; Jiffy? parseyyyymmddhhmm() => length >= 16 ? Jiffy.parse(this, pattern: 'yyyy-MM-dd HH:mm:ss', // "${substring(6, 10)}-${substring(3, 5)}-${substring(0, 2)} ${substring(11, 13)}${substring(13, 16)}", // pattern: 'yyyy-MM-dd HH:mm', isUtc: true) : null; } extension BuildContextExt on BuildContext { ScaffoldFeatureController showAlert( String message, ) => ScaffoldMessenger.of(this).showSnackBar( SnackBar( content: Text( message, ), duration: const Duration(milliseconds: 5000), ), ); ScaffoldFeatureController showError( String message, ) => ScaffoldMessenger.of(this).showSnackBar( SnackBar( backgroundColor: Colors.red, content: Text( message, style: const TextStyle( color: Colors.white, fontWeight: FontWeight.w700), ), duration: const Duration(milliseconds: 5000), ), ); ScaffoldFeatureController showSuccess( String message, ) => ScaffoldMessenger.of(this).showSnackBar( SnackBar( backgroundColor: Colors.green, content: Text( message, style: const TextStyle( color: Colors.white, fontWeight: FontWeight.w700), ), duration: const Duration(milliseconds: 5000), ), ); Future showBottomSheet({ required Widget child, }) => showModalBottomSheet( context: this, builder: (_) => child, ); } extension AlertDialogExt on AlertDialog { dynamic show(BuildContext context) => showDialog( context: context, builder: (BuildContext mycontext) => this, ); } class ClockNotifier extends StateNotifier { ClockNotifier() : super(Jiffy.now()) { timer = Timer.periodic(const Duration(minutes: 1), (timer) { state = Jiffy.now().toUtc(); }); } @override void dispose() { timer.cancel(); super.dispose(); } late final Timer timer; } final StateNotifierProvider clockProvider = StateNotifierProvider( (_) => ClockNotifier(), ); class PathTo { static final PathTo _instance = PathTo._internal(); factory PathTo() => _instance; PathTo._internal(); late Directory _doc; Future initialize() async { _doc = await getApplicationDocumentsDirectory(); for (var dir in dirs) { if (!subd(dir).existsSync()) { subd(dir).createSync(recursive: true); } } } Directory subd(dynamic x) { if (x is String) { return Directory("${_doc.path}/$x"); // } else if (x is Directory) { // return Directory("${_doc.path}/${x.path}"); } else if (x is List) { return subd(x.join("/")); } else { throw ("subd : x is not String nor List"); } } final List dirs = ["crewlink", "lido", "csv", "download"]; String crewlinkFile(String file) => subd(["crewlink", file]).path; String csvFile(String file) => subd(["csv", file]).path; String lidoFile(String file) => subd(["lido", file]).path; String downloadFile(String file) => subd(["download", file]).path; } class DTInterval { late Jiffy start; late Jiffy end; DTInterval(this.start, this.end); @override String toString() => "<${start.format(pattern: "ddMMMyy HH:mm")} - ${end.format(pattern: "ddMMMyy HH:mm")}>"; //apartir has to be utc DTInterval.fromHm({ required Jiffy apartir, required int h, required int m, required Duration duration, required String ap, }) { final Jiffy apartirCorrected = apartir.clone().setTz(ap: ap); Jiffy newval = Jiffy.parseFromDateTime( apartirCorrected.dateTime.copyWith(hour: h, minute: m)); if (newval.isBefore(apartirCorrected)) newval = newval.add(days: 1); start = newval.toUtc(); end = start.addDuration(duration).toUtc(); } // DTInterval.fromHm({ // required Jiffy apartir, // required int h, // required int m, // required Duration duration, // }) { // Jiffy newval = Jiffy.parseFromDateTime( // apartir.clone().dateTime.copyWith(hour: h, minute: m)); // if (newval.isBefore(apartir)) newval = newval.add(days: 1); // start = newval; // end = start.addDuration(duration); // } bool include(Jiffy x) { return x.isSameOrAfter(start) && x.isSameOrBefore(end); } bool isOverlap(DTInterval x) { return x.start.isSameOrBefore(end) && x.end.isSameOrAfter(start); } bool contains(DTInterval x) { return include(x.start) && include(x.end); } DTInterval? intersection(DTInterval x) { if (!isOverlap(x)) { return null; } else { return DTInterval(start.max(x.start), end.min(x.end)); } } List intersectionmany(List x) { return x.map((e) => intersection(e)).nonNulls.toList(); } List minus(DTInterval x) { if (!isOverlap(x)) { return [this]; } else if (x.include(start) && x.include(end)) { return []; } else if (x.include(start)) { return [DTInterval(x.end, end)]; } else if (x.include(end)) { return [DTInterval(start, x.start)]; } else { return [DTInterval(start, x.start), DTInterval(x.end, end)]; } } // List minus(DTInterval other) { // List result = []; // if (start.isBefore(other.start) && end.isAfter(other.start)) { // result.add(DTInterval(start, other.start)); // } // if (start.isBefore(other.end) && end.isAfter(other.end)) { // result.add(DTInterval(other.end, end)); // } // return result; // } List minusmany(List x) { List res = [this]; for (var e in x) { res = res.map((f) => f.minus(e)).flattened.toList(); } return res; } Duration get duration => end.dateTime.difference(start.dateTime); // Duration get duration => Duration( // milliseconds: end.diff(start, unit: Unit.millisecond).abs().ceil()); bool isEmpty() { return start.isSameOrAfter(end); } DTInterval toUtc() { return DTInterval(start.toUtc(), end.toUtc()); } }