From 21ec08303ad19dc8317ed3c7486e96e69f4cc129 Mon Sep 17 00:00:00 2001 From: vjrj Date: Thu, 5 Mar 2026 01:18:27 +0100 Subject: [PATCH] Last changes not published --- android/app/build.gradle | 8 +- .../java/org/comunes/fires/MainActivity.java | 10 +- android/app/src/main/res/values/styles.xml | 4 + android/build.gradle | 4 +- android/gradle.properties | 2 + .../gradle/wrapper/gradle-wrapper.properties | 2 +- build.yaml | 13 +- lib/activeFires.dart | 10 +- lib/attributionMapPlugin.dart | 4 +- lib/customBottomAppBar.dart | 19 +- lib/customStepper.dart | 186 +++++++++--------- lib/dummyMapPlugin.dart | 7 +- lib/fileUtils.dart | 31 ++- lib/fireAlert.dart | 27 +-- lib/fireMarker.dart | 27 +-- lib/fireNotificationList.dart | 60 +++--- lib/firesApp.dart | 13 +- lib/genericMap.dart | 71 +++---- lib/genericMapBottom.dart | 12 +- lib/homePage.dart | 118 +++++------ lib/layerSelectorMapPlugin.dart | 12 +- lib/locationUtils.dart | 36 ++-- lib/mainCommon.dart | 28 +-- lib/mainDev.dart | 6 +- lib/mainDrawer.dart | 31 ++- lib/models/appState.dart | 14 +- lib/models/appState.g.dart | 59 +++--- lib/models/fireNotification.dart | 9 +- lib/models/fireNotification.g.dart | 90 +++------ lib/models/fireNotificationsPersist.dart | 5 +- lib/models/firesApi.dart | 77 ++++---- lib/models/yourLocation.dart | 39 ++-- lib/models/yourLocation.g.dart | 72 +++---- lib/models/yourLocationPersist.dart | 5 +- lib/objectIdUtils.dart | 7 +- lib/placesAutocompleteUtils.dart | 8 +- lib/redux/fetchDataMiddleware.dart | 21 +- lib/redux/fireNotificationReducer.dart | 5 +- lib/redux/yourLocationActions.dart | 2 +- lib/theme.dart | 2 +- lib/themeDev.dart | 2 +- lib/zoomMapPlugin.dart | 8 +- pubspec.yaml | 54 ++--- 43 files changed, 607 insertions(+), 613 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 26178e4..437fd9e 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -8,7 +8,7 @@ if (localPropertiesFile.exists()) { def flutterRoot = localProperties.getProperty('flutter.sdk') if (flutterRoot == null) { - throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") + throw new FileNotFoundException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") } apply plugin: 'com.android.application' @@ -19,7 +19,7 @@ def keystoreProperties = new Properties() keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) android { - compileSdkVersion 27 + compileSdkVersion 30 lintOptions { disable 'InvalidPackage' @@ -29,7 +29,7 @@ android { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). applicationId "org.comunes.fires" minSdkVersion 16 - targetSdkVersion 27 + targetSdkVersion 30 versionCode 9 versionName "1.9" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" @@ -78,7 +78,7 @@ dependencies { testImplementation 'junit:junit:4.12' androidTestImplementation 'com.android.support.test:runner:1.0.1' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1' - implementation 'com.google.firebase:firebase-core:15.0.2' + // implementation 'com.google.firebase:firebase-core:15.0.2' // google recommends 16 but fails because location flutter package deps in 15 } diff --git a/android/app/src/main/java/org/comunes/fires/MainActivity.java b/android/app/src/main/java/org/comunes/fires/MainActivity.java index 0caf39c..1d51087 100644 --- a/android/app/src/main/java/org/comunes/fires/MainActivity.java +++ b/android/app/src/main/java/org/comunes/fires/MainActivity.java @@ -1,14 +1,6 @@ package org.comunes.fires; -import android.os.Bundle; - -import io.flutter.app.FlutterActivity; -import io.flutter.plugins.GeneratedPluginRegistrant; +import io.flutter.embedding.android.FlutterActivity; public class MainActivity extends FlutterActivity { - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - GeneratedPluginRegistrant.registerWith(this); - } } diff --git a/android/app/src/main/res/values/styles.xml b/android/app/src/main/res/values/styles.xml index 00fa441..af831f5 100644 --- a/android/app/src/main/res/values/styles.xml +++ b/android/app/src/main/res/values/styles.xml @@ -5,4 +5,8 @@ Flutter draws its first frame --> @drawable/launch_background + + diff --git a/android/build.gradle b/android/build.gradle index abea04c..7013083 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -5,8 +5,8 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:3.0.1' - classpath 'com.google.gms:google-services:4.0.1' + classpath 'com.android.tools.build:gradle:4.1.0' + classpath 'com.google.gms:google-services:4.3.4' } } diff --git a/android/gradle.properties b/android/gradle.properties index 8bd86f6..94adc3a 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1 +1,3 @@ org.gradle.jvmargs=-Xmx1536M +android.useAndroidX=true +android.enableJetifier=true diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index aa901e1..bc6a58a 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip diff --git a/build.yaml b/build.yaml index 3c7137f..c1992bf 100644 --- a/build.yaml +++ b/build.yaml @@ -9,11 +9,9 @@ targets: # defined in `package:source_gen/source_gen.dart`. # # Note: use `|` to define a multi-line block. - header: | - // Copyright (c) 2018, Comunes Association. - - // GENERATED CODE - DO NOT MODIFY BY HAND - + # header: | + # // Copyright (c) 2018, Comunes Association. + # // GENERATED CODE - DO NOT MODIFY BY HAND # Options configure how source code is generated for every # `@JsonSerializable`-annotated class in the package. # @@ -21,8 +19,9 @@ targets: # # For usage information, reference the corresponding field in # `JsonSerializableGenerator`. - use_wrappers: true + #use_wrappers: true any_map: true checked: true explicit_to_json: true - generate_to_json_function: true + #generate_to_json_function: true + create_to_json: true diff --git a/lib/activeFires.dart b/lib/activeFires.dart index f440a44..5ae2871 100644 --- a/lib/activeFires.dart +++ b/lib/activeFires.dart @@ -5,7 +5,7 @@ import 'package:fires_flutter/models/yourLocation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_fab_dialer/flutter_fab_dialer.dart'; import 'package:flutter_redux/flutter_redux.dart'; -import 'package:redux/src/store.dart'; +import 'package:redux/redux.dart'; import 'colors.dart'; import 'firesSpinner.dart'; @@ -91,7 +91,7 @@ class _ActiveFiresPageState extends State { icon: new Icon(loc.subscribed ? Icons.notifications_active : Icons.notifications_off), - color: loc.subscribed ? fires600: null, + color: loc.subscribed ? fires600 : null, onPressed: () { loc.subscribed = !loc.subscribed; onToggle(loc); @@ -125,7 +125,8 @@ class _ActiveFiresPageState extends State { } void showSnackMsg(String msg) { - _scaffoldKey.currentState.showSnackBar(new SnackBar( + // _scaffoldKey.currentState.showSnackBar(new SnackBar( + ScaffoldMessenger.of(context).showSnackBar(new SnackBar( content: new Text(msg), )); } @@ -183,7 +184,7 @@ class _ActiveFiresPageState extends State { }, onDelete: (loc) { store.dispatch(new DeleteYourLocationAction(loc)); - _scaffoldKey.currentState.showSnackBar(new SnackBar( + ScaffoldMessenger.of(context).showSnackBar(new SnackBar( content: new Text(S.of(context).youDeletedThisPlace), action: new SnackBarAction( label: S.of(context).UNDO, @@ -242,6 +243,7 @@ class _ActiveFiresPageState extends State { view.onRefresh(completer); return completer.future; }), + // TODO: Evaluate: https://github.com/tiagojencmartins/unicornspeeddial new FabDialer(_fabMiniMenuItemList(context, view.onAdd), fires600, new Icon(Icons.add)) ]) diff --git a/lib/attributionMapPlugin.dart b/lib/attributionMapPlugin.dart index a33b457..b10534a 100644 --- a/lib/attributionMapPlugin.dart +++ b/lib/attributionMapPlugin.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:flutter/src/widgets/framework.dart'; import 'package:flutter_map/plugin_api.dart'; class AttributionPluginOptions extends LayerOptions { @@ -10,7 +9,8 @@ class AttributionPluginOptions extends LayerOptions { class AttributionPlugin implements MapPlugin { @override - Widget createLayer(LayerOptions options, MapState mapState) { + Widget createLayer( + LayerOptions options, MapState mapState, Stream stream) { if (options is AttributionPluginOptions) { var style = new TextStyle( // fontWeight: FontWeight.bold, diff --git a/lib/customBottomAppBar.dart b/lib/customBottomAppBar.dart index 162a433..48fa5a7 100644 --- a/lib/customBottomAppBar.dart +++ b/lib/customBottomAppBar.dart @@ -34,12 +34,17 @@ class CustomBottomAppBar extends StatelessWidget { rowContents.addAll(this.actions); - return new BottomAppBar( - color: color, - hasNotch: showNotch, - child: new Row( - mainAxisAlignment: mainAxisAlignment, - children: rowContents), - ); + return showNotch + ? new BottomAppBar( + color: color, + shape: CircularNotchedRectangle(), + child: new Row( + mainAxisAlignment: mainAxisAlignment, children: rowContents), + ) + : new BottomAppBar( + color: color, + child: new Row( + mainAxisAlignment: mainAxisAlignment, children: rowContents), + ); } } diff --git a/lib/customStepper.dart b/lib/customStepper.dart index 5d0c3fd..4fafac3 100644 --- a/lib/customStepper.dart +++ b/lib/customStepper.dart @@ -3,7 +3,6 @@ // found in the LICENSE file. import 'package:flutter/material.dart'; - // TODO(dragostis): Missing functionality: // * mobile horizontal mode with adding/removing steps // * alternative labeling @@ -53,7 +52,8 @@ const Color _kCircleActiveDark = Colors.black87; const Color _kDisabledLight = Colors.black38; const Color _kDisabledDark = Colors.white30; const double _kCustomStepSize = 24.0; -const double _kTriangleHeight = _kCustomStepSize * 0.866025; // Triangle height. sqrt(3.0) / 2.0 +const double _kTriangleHeight = + _kCustomStepSize * 0.866025; // Triangle height. sqrt(3.0) / 2.0 /// A material step used in [CustomStepper]. The step can have a title and subtitle, /// an icon within its circle, some content and a state that governs its @@ -75,9 +75,9 @@ class CustomStep { @required this.content, this.state: CustomStepState.indexed, this.isActive: false, - }) : assert(title != null), - assert(content != null), - assert(state != null); + }) : assert(title != null), + assert(content != null), + assert(state != null); /// The title of the step that typically describes it. final Widget title; @@ -130,11 +130,11 @@ class CustomStepper extends StatefulWidget { this.onCustomStepTapped, this.onCustomStepContinue, this.onCustomStepCancel, - }) : assert(steps != null), - assert(type != null), - assert(currentCustomStep != null), - assert(0 <= currentCustomStep && currentCustomStep < steps.length), - super(key: key); + }) : assert(steps != null), + assert(type != null), + assert(currentCustomStep != null), + assert(0 <= currentCustomStep && currentCustomStep < steps.length), + super(key: key); /// The steps of the stepper whose titles, subtitles, icons always get shown. /// @@ -177,7 +177,7 @@ class _CustomStepperState extends State { super.initState(); _keys = new List.generate( widget.steps.length, - (int i) => new GlobalKey(), + (int i) => new GlobalKey(), ); for (int i = 0; i < widget.steps.length; i += 1) @@ -218,7 +218,8 @@ class _CustomStepperState extends State { } Widget _buildCircleChild(int index, bool oldState) { - final CustomStepState state = oldState ? _oldStates[index] : widget.steps[index].state; + final CustomStepState state = + oldState ? _oldStates[index] : widget.steps[index].state; final bool isDarkActive = _isDark() && widget.steps[index].isActive; assert(state != null); switch (state) { @@ -226,7 +227,9 @@ class _CustomStepperState extends State { case CustomStepState.disabled: return new Text( '${index + 1}', - style: isDarkActive ? _kCustomStepStyle.copyWith(color: Colors.black87) : _kCustomStepStyle, + style: isDarkActive + ? _kCustomStepStyle.copyWith(color: Colors.black87) + : _kCustomStepStyle, ); case CustomStepState.editing: return new Icon( @@ -247,9 +250,13 @@ class _CustomStepperState extends State { Color _circleColor(int index) { final ThemeData themeData = Theme.of(context); if (!_isDark()) { - return widget.steps[index].isActive ? themeData.primaryColor : Colors.black38; + return widget.steps[index].isActive + ? themeData.primaryColor + : Colors.black38; } else { - return widget.steps[index].isActive ? themeData.accentColor : themeData.backgroundColor; + return widget.steps[index].isActive + ? themeData.accentColor + : themeData.backgroundColor; } } @@ -266,7 +273,8 @@ class _CustomStepperState extends State { shape: BoxShape.circle, ), child: new Center( - child: _buildCircleChild(index, oldState && widget.steps[index].state == CustomStepState.error), + child: _buildCircleChild(index, + oldState && widget.steps[index].state == CustomStepState.error), ), ), ); @@ -280,14 +288,19 @@ class _CustomStepperState extends State { child: new Center( child: new SizedBox( width: _kCustomStepSize, - height: _kTriangleHeight, // Height of 24dp-long-sided equilateral triangle. + height: + _kTriangleHeight, // Height of 24dp-long-sided equilateral triangle. child: new CustomPaint( painter: new _TrianglePainter( color: _isDark() ? _kErrorDark : _kErrorLight, ), child: new Align( - alignment: const Alignment(0.0, 0.8), // 0.8 looks better than the geometrical 0.33. - child: _buildCircleChild(index, oldState && widget.steps[index].state != CustomStepState.error), + alignment: const Alignment( + 0.0, 0.8), // 0.8 looks better than the geometrical 0.33. + child: _buildCircleChild( + index, + oldState && + widget.steps[index].state != CustomStepState.error), ), ), ), @@ -303,7 +316,9 @@ class _CustomStepperState extends State { firstCurve: const Interval(0.0, 0.6, curve: Curves.fastOutSlowIn), secondCurve: const Interval(0.4, 1.0, curve: Curves.fastOutSlowIn), sizeCurve: Curves.fastOutSlowIn, - crossFadeState: widget.steps[index].state == CustomStepState.error ? CrossFadeState.showSecond : CrossFadeState.showFirst, + crossFadeState: widget.steps[index].state == CustomStepState.error + ? CrossFadeState.showSecond + : CrossFadeState.showFirst, duration: kThemeAnimationDuration, ); } else { @@ -328,8 +343,9 @@ class _CustomStepperState extends State { assert(cancelColor != null); - final ThemeData themeData = Theme.of(context); - final MaterialLocalizations localizations = MaterialLocalizations.of(context); + // final ThemeData themeData = Theme.of(context); + // final MaterialLocalizations localizations = + // MaterialLocalizations.of(context); return new Container( margin: const EdgeInsets.only(top: 16.0), @@ -368,15 +384,13 @@ class _CustomStepperState extends State { case CustomStepState.indexed: case CustomStepState.editing: case CustomStepState.complete: - return textTheme.body2; + return textTheme.bodyText1; case CustomStepState.disabled: - return textTheme.body2.copyWith( - color: _isDark() ? _kDisabledDark : _kDisabledLight - ); + return textTheme.bodyText1 + .copyWith(color: _isDark() ? _kDisabledDark : _kDisabledLight); case CustomStepState.error: - return textTheme.body2.copyWith( - color: _isDark() ? _kErrorDark : _kErrorLight - ); + return textTheme.bodyText1 + .copyWith(color: _isDark() ? _kErrorDark : _kErrorLight); } return null; } @@ -392,13 +406,11 @@ class _CustomStepperState extends State { case CustomStepState.complete: return textTheme.caption; case CustomStepState.disabled: - return textTheme.caption.copyWith( - color: _isDark() ? _kDisabledDark : _kDisabledLight - ); + return textTheme.caption + .copyWith(color: _isDark() ? _kDisabledDark : _kDisabledLight); case CustomStepState.error: - return textTheme.caption.copyWith( - color: _isDark() ? _kErrorDark : _kErrorLight - ); + return textTheme.caption + .copyWith(color: _isDark() ? _kErrorDark : _kErrorLight); } return null; } @@ -427,33 +439,26 @@ class _CustomStepperState extends State { ); return new Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: children - ); + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: children); } Widget _buildVerticalHeader(int index) { return new Container( - margin: const EdgeInsets.symmetric(horizontal: 24.0), - child: new Row( - children: [ - new Column( - children: [ - // Line parts are always added in order for the ink splash to - // flood the tips of the connector lines. - _buildLine(!_isFirst(index)), - _buildIcon(index), - _buildLine(!_isLast(index)), - ] - ), + margin: const EdgeInsets.symmetric(horizontal: 24.0), + child: new Row(children: [ + new Column(children: [ + // Line parts are always added in order for the ink splash to + // flood the tips of the connector lines. + _buildLine(!_isFirst(index)), + _buildIcon(index), + _buildLine(!_isLast(index)), + ]), new Container( - margin: const EdgeInsetsDirectional.only(start: 12.0), - child: _buildHeaderText(index) - ) - ] - ) - ); + margin: const EdgeInsetsDirectional.only(start: 12.0), + child: _buildHeaderText(index)) + ])); } Widget _buildVerticalBody(int index) { @@ -493,7 +498,9 @@ class _CustomStepperState extends State { firstCurve: const Interval(0.0, 0.6, curve: Curves.fastOutSlowIn), secondCurve: const Interval(0.4, 1.0, curve: Curves.fastOutSlowIn), sizeCurve: Curves.fastOutSlowIn, - crossFadeState: _isCurrent(index) ? CrossFadeState.showSecond : CrossFadeState.showFirst, + crossFadeState: _isCurrent(index) + ? CrossFadeState.showSecond + : CrossFadeState.showFirst, duration: kThemeAnimationDuration, ), ], @@ -504,29 +511,25 @@ class _CustomStepperState extends State { final List children = []; for (int i = 0; i < widget.steps.length; i += 1) { - children.add( - new Column( - key: _keys[i], - children: [ - new InkWell( - onTap: widget.steps[i].state != CustomStepState.disabled ? () { - // In the vertical case we need to scroll to the newly tapped - // step. - Scrollable.ensureVisible( - _keys[i].currentContext, - curve: Curves.fastOutSlowIn, - duration: kThemeAnimationDuration, - ); + children.add(new Column(key: _keys[i], children: [ + new InkWell( + onTap: widget.steps[i].state != CustomStepState.disabled + ? () { + // In the vertical case we need to scroll to the newly tapped + // step. + Scrollable.ensureVisible( + _keys[i].currentContext, + curve: Curves.fastOutSlowIn, + duration: kThemeAnimationDuration, + ); - if (widget.onCustomStepTapped != null) - widget.onCustomStepTapped(i); - } : null, - child: _buildVerticalHeader(i) - ), - _buildVerticalBody(i) - ] - ) - ); + if (widget.onCustomStepTapped != null) + widget.onCustomStepTapped(i); + } + : null, + child: _buildVerticalHeader(i)), + _buildVerticalBody(i) + ])); } return new ListView( @@ -541,10 +544,12 @@ class _CustomStepperState extends State { for (int i = 0; i < widget.steps.length; i += 1) { children.add( new InkResponse( - onTap: widget.steps[i].state != CustomStepState.disabled ? () { - if (widget.onCustomStepTapped != null) - widget.onCustomStepTapped(i); - } : null, + onTap: widget.steps[i].state != CustomStepState.disabled + ? () { + if (widget.onCustomStepTapped != null) + widget.onCustomStepTapped(i); + } + : null, child: new Row( children: [ new Container( @@ -593,7 +598,7 @@ class _CustomStepperState extends State { new AnimatedSize( curve: Curves.fastOutSlowIn, duration: kThemeAnimationDuration, - //vsync: this, + vsync: null, child: widget.steps[widget.currentCustomStep].content, ), _buildVerticalControls(), @@ -608,12 +613,11 @@ class _CustomStepperState extends State { Widget build(BuildContext context) { assert(debugCheckHasMaterial(context)); assert(() { - if (context.ancestorWidgetOfExactType(CustomStepper) != null) + if (context.findAncestorWidgetOfExactType() != null) throw new FlutterError( - 'CustomSteppers must not be nested. The material specification advises ' + 'CustomSteppers must not be nested. The material specification advises ' 'that one should avoid embedding steppers within steppers. ' - 'https://material.google.com/components/steppers.html#steppers-usage\n' - ); + 'https://material.google.com/components/steppers.html#steppers-usage\n'); return true; }()); assert(widget.type != null); @@ -630,9 +634,7 @@ class _CustomStepperState extends State { // Paints a triangle whose base is the bottom of the bounding rectangle and its // top vertex the middle of its top. class _TrianglePainter extends CustomPainter { - _TrianglePainter({ - this.color - }); + _TrianglePainter({this.color}); final Color color; diff --git a/lib/dummyMapPlugin.dart b/lib/dummyMapPlugin.dart index 565d549..6a1df20 100644 --- a/lib/dummyMapPlugin.dart +++ b/lib/dummyMapPlugin.dart @@ -1,6 +1,5 @@ -import 'package:flutter/src/widgets/framework.dart'; -import 'package:flutter_map/plugin_api.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_map/plugin_api.dart'; class DummyMapPluginOptions extends LayerOptions { final String text; @@ -10,9 +9,9 @@ class DummyMapPluginOptions extends LayerOptions { // https://github.com/apptreesoftware/flutter_map/blob/master/flutter_map_example/lib/pages/plugin_api.dart class DummyMapPlugin extends MapPlugin { - @override - Widget createLayer(LayerOptions options, MapState mapState) { + Widget createLayer( + LayerOptions options, MapState mapState, Stream stream) { if (options is DummyMapPluginOptions) { return const SizedBox(); } diff --git a/lib/fileUtils.dart b/lib/fileUtils.dart index 54a8151..8950bab 100644 --- a/lib/fileUtils.dart +++ b/lib/fileUtils.dart @@ -1,25 +1,34 @@ import 'dart:async'; + import 'package:flutter/services.dart' show rootBundle; import 'package:meta/meta.dart'; final esRegExp = new RegExp("^es-"); -String getFallbackLang(String lang) { - if (lang == 'ast' || lang == 'gl' || lang == 'eu' || - lang == 'es' || lang == 'ca' || (esRegExp).allMatches(lang).length > 0) { +String getFallbackLang(String lang) { + if (lang == 'ast' || + lang == 'gl' || + lang == 'eu' || + lang == 'es' || + lang == 'ca' || + (esRegExp).allMatches(lang).length > 0) { return 'es'; } return 'en'; } -Future getFileNameOfLang({@required String dir,@required String fileName,@required String ext,@required String lang}) async { +Future getFileNameOfLang( + {@required String dir, + @required String fileName, + @required String ext, + @required String lang}) async { String base = '$dir/$fileName'; String fallback = getFallbackLang(lang); - String file = '${base}-${lang}.${ext}'; + String file = '$base-$lang.$ext'; if (await assetNotExists(file)) { - file = '${base}-${fallback}.${ext}'; + file = '$base-$fallback.$ext'; if (await assetNotExists(file)) { - file = '${base}.${ext}'; + file = '$base.$ext'; } } return file; @@ -28,5 +37,9 @@ Future getFileNameOfLang({@required String dir,@required String fileNam // https://github.com/flutter/flutter/issues/15325 Future assetNotExists(String asset) { print('Does not exists $asset ?'); - return rootBundle.load(asset).then((_) => false).catchError((err, stack) { print (err); print(stack); return true;}); -} \ No newline at end of file + return rootBundle.load(asset).then((_) => false).catchError((err, stack) { + print(err); + print(stack); + return true; + }); +} diff --git a/lib/fireAlert.dart b/lib/fireAlert.dart index 3fd2322..87bb76b 100644 --- a/lib/fireAlert.dart +++ b/lib/fireAlert.dart @@ -3,10 +3,10 @@ import 'package:comunes_flutter/comunes_flutter.dart'; import 'package:flutter/material.dart'; import 'package:share/share.dart'; import 'package:url_launcher/url_launcher.dart'; -import 'customStepper.dart'; -import 'mainDrawer.dart'; +import 'customStepper.dart'; import 'generated/i18n.dart'; +import 'mainDrawer.dart'; import 'placesAutocompleteUtils.dart'; class FireAlert extends StatefulWidget { @@ -40,7 +40,7 @@ class _FireAlertState extends State { heroTag: 'neighAction', child: const Icon(CommunityMaterialIcons.bullhorn), onPressed: () { - _scaffoldKey.currentState.showSnackBar(new SnackBar( + ScaffoldMessenger.of(context).showSnackBar(new SnackBar( content: new Text(S.of(context).inDevelopment), )); }, @@ -48,7 +48,6 @@ class _FireAlertState extends State { ); } - Widget buildTweetButton() { return new Align( alignment: const Alignment(0.0, -0.2), @@ -62,9 +61,11 @@ class _FireAlertState extends State { String where = yourLocation.description.replaceAll(' ', '').split(',')[0]; print(where); - Share.share(S.of(context).tweetAboutSelf(yourLocation.description, '#IF${where}')); + Share.share(S + .of(context) + .tweetAboutSelf(yourLocation.description, '#IF$where')); }).catchError((onError) { - _scaffoldKey.currentState.showSnackBar(new SnackBar( + ScaffoldMessenger.of(context).showSnackBar(new SnackBar( content: new Text( S.of(_scaffoldKey.currentContext).errorFirePlaceDialog))); }); @@ -87,13 +88,13 @@ class _FireAlertState extends State { buildCallButton() ])), new CustomStep( - title: new Text(S.of(context).notifyNeighbours), - // state: CustomStepState.disabled, - content: new Column(children: [ - new Text(S.of(context).notifyNeighboursDescription), - new SizedBox(height: 20.0), - buildNotifyNeighboursButton() - ])), + title: new Text(S.of(context).notifyNeighbours), + // state: CustomStepState.disabled, + content: new Column(children: [ + new Text(S.of(context).notifyNeighboursDescription), + new SizedBox(height: 20.0), + buildNotifyNeighboursButton() + ])), // TODO conditional: this only in Spain new CustomStep( title: new Text(S.of(context).tweetAboutAFireTitle), diff --git a/lib/fireMarker.dart b/lib/fireMarker.dart index a11092b..df4f584 100644 --- a/lib/fireMarker.dart +++ b/lib/fireMarker.dart @@ -2,30 +2,33 @@ import 'package:flutter/material.dart'; import 'package:flutter_map/flutter_map.dart'; import 'package:flutter_map/plugin_api.dart'; import 'package:latlong/latlong.dart'; -import 'fireMarkerIcon.dart'; + import 'fireMarkType.dart'; +import 'fireMarkerIcon.dart'; class FireMarker extends Marker { - FireMarker(LatLng pos, type, - [onTap = null, AnchorPos anchor = AnchorPos.center, Anchor anchorOverride]) + [onTap, + // AnchorPos anchor = AnchorPos.center, + Anchor anchorOverride]) : super( width: 80.0, height: 80.0, point: pos, builder: (ctx) => new Container( - child: new GestureDetector( - child: new FireMarkerIcon(type), onTap: onTap) - ), - anchor: anchor, - anchorOverride: anchorOverride ?? type == FireMarkType.position - ? new Anchor(40.0, 20.0) + child: new GestureDetector( + child: new FireMarkerIcon(type), onTap: onTap)), + // anchor: anchor, + anchorPos: anchorOverride ?? type == FireMarkType.position + ? AnchorPos.exactly(new Anchor(40.0, 20.0)) : type == FireMarkType.fire - ? new Anchor(40.0, 24.0) + ? AnchorPos.exactly(new Anchor(40.0, 24.0)) : type == FireMarkType.pixel ? null // auto calculate with height/width in Marker // industries, etc (below) // FIXME check if this is accurate - : new Anchor(40.0, 35.0), - ); + : AnchorPos.exactly(new Anchor(40.0, 35.0)), + ) { + // anchor = anchorOverride; + } } diff --git a/lib/fireNotificationList.dart b/lib/fireNotificationList.dart index da131e5..dfb5d6d 100644 --- a/lib/fireNotificationList.dart +++ b/lib/fireNotificationList.dart @@ -5,15 +5,15 @@ import 'package:fires_flutter/models/fireNotification.dart'; import 'package:fires_flutter/models/yourLocation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_redux/flutter_redux.dart'; -import 'package:redux/src/store.dart'; +import 'package:redux/redux.dart'; import 'customMoment.dart'; +import 'firesSpinner.dart'; import 'generated/i18n.dart'; import 'genericMap.dart'; import 'mainDrawer.dart'; import 'models/appState.dart'; import 'redux/actions.dart'; -import 'firesSpinner.dart'; @immutable class _ViewModel { @@ -76,7 +76,7 @@ class _FireNotificationListState extends State { return new ListTile( dense: true, leading: const Icon(Icons.whatshot), - title: new Text('${prefix}${notif.description}', + title: new Text('$prefix${notif.description}', style: new TextStyle( fontWeight: notif.read ? FontWeight.normal : FontWeight.bold)), subtitle: new Text(Moment.now().from(context, notif.when)), @@ -89,7 +89,7 @@ class _FireNotificationListState extends State { } void showSnackMsg(String msg) { - _scaffoldKey.currentState.showSnackBar(new SnackBar( + ScaffoldMessenger.of(context).showSnackBar(new SnackBar( content: new Text(msg), )); } @@ -149,8 +149,8 @@ class _FireNotificationListState extends State { return new StoreConnector( distinct: true, converter: (store) { - print('New ViewModel of Fires Notifications (unread: ${store.state - .fireNotificationsUnread})'); + print( + 'New ViewModel of Fires Notifications (unread: ${store.state.fireNotificationsUnread})'); return new _ViewModel( isLoaded: store.state.isLoaded, onDeleteAll: () { @@ -158,7 +158,7 @@ class _FireNotificationListState extends State { }, onDelete: (notif) { store.dispatch(new DeleteFireNotificationAction(notif)); - _scaffoldKey.currentState.showSnackBar(new SnackBar( + ScaffoldMessenger.of(context).showSnackBar(new SnackBar( content: new Text(S.of(context).youDeletedThisNotification), action: new SnackBarAction( label: S.of(context).UNDO, @@ -202,25 +202,33 @@ class _FireNotificationListState extends State { onPressed: () => _showConfirmDialog(view)) : null ])), - body: !view.isLoaded ? new FiresSpinner() : !hasFireNotifications - ? Padding( - padding: const EdgeInsets.all(20.0), - child: new Card( - child: new Padding( - padding: const EdgeInsets.all(20.0), - child: new CenteredColumn(children: [ - new Icon(Icons.notifications_none, - size: 150.0, color: Colors.black26), - new SizedBox(height: 20.0), - new Text( - S.of(context).fireNotificationsDescription, - textAlign: TextAlign.center, - textScaleFactor: 1.1, - style: new TextStyle( - height: 1.3, color: Colors.black45)) - ])))) - : _buildSavedFireNotifications(context, view.yourLocations, - view.fireNotifications, view.onDelete, view.onTap)); + body: !view.isLoaded + ? new FiresSpinner() + : !hasFireNotifications + ? Padding( + padding: const EdgeInsets.all(20.0), + child: new Card( + child: new Padding( + padding: const EdgeInsets.all(20.0), + child: new CenteredColumn(children: [ + new Icon(Icons.notifications_none, + size: 150.0, color: Colors.black26), + new SizedBox(height: 20.0), + new Text( + S + .of(context) + .fireNotificationsDescription, + textAlign: TextAlign.center, + textScaleFactor: 1.1, + style: new TextStyle( + height: 1.3, color: Colors.black45)) + ])))) + : _buildSavedFireNotifications( + context, + view.yourLocations, + view.fireNotifications, + view.onDelete, + view.onTap)); }); } diff --git a/lib/firesApp.dart b/lib/firesApp.dart index 5d0d4f2..4952e66 100644 --- a/lib/firesApp.dart +++ b/lib/firesApp.dart @@ -1,20 +1,18 @@ - import 'package:comunes_flutter/comunes_flutter.dart'; import 'package:flutter/material.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:flutter_redux/flutter_redux.dart'; import 'package:redux/redux.dart'; -import 'package:redux/src/store.dart'; -import 'globals.dart'; -import 'monitoredAreas.dart'; import 'activeFires.dart'; import 'fireAlert.dart'; import 'fireNotificationList.dart'; import 'generated/i18n.dart'; +import 'globals.dart'; import 'homePage.dart'; import 'introPage.dart'; import 'models/appState.dart'; +import 'monitoredAreas.dart'; import 'privacyPage.dart'; import 'redux/actions.dart'; import 'sandbox.dart'; @@ -47,16 +45,15 @@ class _FiresAppState extends State { SupportPage.routeName: (BuildContext context) => new SupportPage(), FireNotificationList.routeName: (BuildContext context) => new FireNotificationList(), - MonitoredAreasPage.routeName: (BuildContext context) => new MonitoredAreasPage() + MonitoredAreasPage.routeName: (BuildContext context) => + new MonitoredAreasPage() }; final Store store; - // globals.getIt.registerSingleton _FiresAppState(this.store); - @override Widget build(BuildContext context) { StatefulWidget home = new MaterialAppWithIntroHome( @@ -82,7 +79,7 @@ class _FiresAppState extends State { } return S.of(context).appName; }, - theme: isDevelopment? devFiresTheme: firesTheme, + theme: isDevelopment ? devFiresTheme : firesTheme, routes: routes)); } } diff --git a/lib/genericMap.dart b/lib/genericMap.dart index 7b74864..6f181ac 100644 --- a/lib/genericMap.dart +++ b/lib/genericMap.dart @@ -1,5 +1,5 @@ import 'dart:core'; -import 'locationUtils.dart'; + import 'package:comunes_flutter/comunes_flutter.dart'; import 'package:fires_flutter/models/yourLocation.dart'; import 'package:flutter/material.dart'; @@ -11,6 +11,7 @@ import 'package:share/share.dart'; import 'attributionMapPlugin.dart'; import 'colors.dart'; +import 'customMoment.dart'; import 'dummyMapPlugin.dart'; import 'fireMarkType.dart'; import 'fireMarker.dart'; @@ -18,13 +19,13 @@ import 'generated/i18n.dart'; import 'genericMapBottom.dart'; import 'globals.dart' as globals; import 'layerSelectorMapPlugin.dart'; +import 'locationUtils.dart'; import 'models/appState.dart'; import 'models/fireMapState.dart'; import 'redux/actions.dart'; import 'sentryReport.dart'; import 'slider.dart'; import 'zoomMapPlugin.dart'; -import 'customMoment.dart'; @immutable class _ViewModel { @@ -142,33 +143,34 @@ class _genericMapState extends State { double maxZoom = 18.0; // works? MapOptions mapOptions = new MapOptions( - center: new LatLng(_location.lat, _location.lon), - plugins: globals.isDevelopment - ? [ - new ZoomMapPlugin(), - new AttributionPlugin(), - new LayerSelectorMapPlugin() - ] - : [ - new DummyMapPlugin(), - new AttributionPlugin(), - new LayerSelectorMapPlugin() - ], - // this works ? - interactive: false, - zoom: 13.0, - // THIS does not works as expected - maxZoom: maxZoom, - onTap: (callback) { - if (status == FireMapStatus.edit) { - _location = _location.copyWith( - lat: callback.latitude, lon: callback.longitude); - view.onEditing(_location); - } - }, - onPositionChanged: (positionCallback) { - // print('${positionCallback.center}, ${positionCallback.zoom}'); - }); + center: new LatLng(_location.lat, _location.lon), + plugins: globals.isDevelopment + ? [ + new ZoomMapPlugin(), + new AttributionPlugin(), + new LayerSelectorMapPlugin() + ] + : [ + new DummyMapPlugin(), + new AttributionPlugin(), + new LayerSelectorMapPlugin() + ], + // this works ? + interactive: true, + zoom: 13.0, + // THIS does not works as expected + maxZoom: maxZoom, + onTap: (callback) { + if (status == FireMapStatus.edit) { + _location = _location.copyWith( + lat: callback.latitude, lon: callback.longitude); + view.onEditing(_location); + } + }, + // onPositionChanged: (positionCallback) { + // print('${positionCallback.center}, ${positionCallback.zoom}'); + //} + ); var mapController = new MapController(); // mapController.fitBounds(bounds); @@ -312,7 +314,7 @@ class _genericMapState extends State { } }, // https://github.com/flutter/flutter/issues/17583 - heroTag: "firesmap" + _location.id.toHexString(), + heroTag: "firesmap" + _location.id.hexString, icon: new Icon(btnIcon, color: fires600), label: new Text( btnText, @@ -388,8 +390,7 @@ class _genericMapState extends State { icon: new Icon(Icons.share), onPressed: () { Share.share( - '${view.mapState.fireNotification.description}. ${view - .serverUrl}fire/${view.mapState.fireNotification.sealed}'); + '${view.mapState.fireNotification.description}. ${view.serverUrl}fire/${view.mapState.fireNotification.sealed}'); }) ]; default: @@ -405,14 +406,14 @@ class _genericMapState extends State { bool isNotif, OnFirePressedInMap onFirePressed) { List markers = []; - print('building markers: fires: ${fires.length} falsePos: ${falsePosList - .length} industries: ${industries.length}, isNotif: ${isNotif} '); + print( + 'building markers: fires: ${fires.length} falsePos: ${falsePosList.length} industries: ${industries.length}, isNotif: $isNotif '); const calibrate = false; // useful when we change the fire icons size falsePosList.forEach((falsePos) { try { var coords = falsePos['geo']['coordinates']; // print('false pos: ${coords}'); - var loc = LatLng(coords[1], coords[0]); + var loc = LatLng(coords[1].toDouble(), coords[0].toDouble()); markers.add(FireMarker(loc, FireMarkType.falsePos)); if (calibrate) markers.add(FireMarker(loc, FireMarkType.pixel)); } catch (e) { diff --git a/lib/genericMapBottom.dart b/lib/genericMapBottom.dart index 9cd257c..8457687 100644 --- a/lib/genericMapBottom.dart +++ b/lib/genericMapBottom.dart @@ -1,8 +1,8 @@ import 'dart:async'; import 'package:comunes_flutter/comunes_flutter.dart'; -import 'package:fires_flutter/models/yourLocation.dart'; import 'package:fires_flutter/models/fireNotification.dart'; +import 'package:fires_flutter/models/yourLocation.dart'; import 'package:flutter/material.dart'; import 'colors.dart'; @@ -27,7 +27,7 @@ class GenericMapBottom extends StatelessWidget { GenericMapBottom( {@required this.onSave, @required this.onCancel, - @required this.onFalsePositive, + @required this.onFalsePositive, @required this.state, @required this.scaffoldKey}); @@ -45,7 +45,7 @@ class GenericMapBottom extends StatelessWidget { List buildActionList(YourLocation loc, BuildContext context, int kmAround, GlobalKey scaffoldState) { - List actionList = new List(); + List actionList = []; switch (state.status) { case FireMapStatus.edit: actionList.add(new FlatButton( @@ -74,8 +74,7 @@ class GenericMapBottom extends StatelessWidget { children: [ new Icon(Icons.access_time), new SizedBox(width: 5.0), - new Text(Moment - .now() + new Text(Moment.now() .from(context, state.fireNotification.when)), ], ), @@ -113,7 +112,8 @@ class GenericMapBottom extends StatelessWidget { onFalsePositive(state.fireNotification, value); await new Future.delayed( new Duration(milliseconds: 500)); - scaffoldKey.currentState.showSnackBar(new SnackBar( + ScaffoldMessenger.of(context) + .showSnackBar(new SnackBar( content: new Text( S.of(context).thanksForParticipating), )); diff --git a/lib/homePage.dart b/lib/homePage.dart index f4c2544..d03bb27 100644 --- a/lib/homePage.dart +++ b/lib/homePage.dart @@ -1,21 +1,21 @@ import 'dart:async'; -import 'package:bson_objectid/bson_objectid.dart'; import 'package:comunes_flutter/comunes_flutter.dart'; import 'package:connectivity/connectivity.dart'; import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:fires_flutter/models/fireNotification.dart'; +import 'package:fires_flutter/objectIdUtils.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_redux/flutter_redux.dart'; import 'package:flutter_simple_dependency_injection/injector.dart'; import 'package:redux/redux.dart'; -import 'firesSpinner.dart'; import 'activeFires.dart'; import 'colors.dart'; import 'fireAlert.dart'; import 'fireNotificationList.dart'; +import 'firesSpinner.dart'; import 'generated/i18n.dart'; import 'mainDrawer.dart'; import 'models/appState.dart'; @@ -28,14 +28,13 @@ class _ViewModel { @override bool operator ==(Object other) => - identical(this, other) || + identical(this, other) || other is _ViewModel && - runtimeType == other.runtimeType && - isLoaded == other.isLoaded; + runtimeType == other.runtimeType && + isLoaded == other.isLoaded; @override int get hashCode => isLoaded.hashCode; - } class HomePage extends StatefulWidget { @@ -83,16 +82,20 @@ class _HomePageState extends State { void initState() { super.initState(); _firebaseMessaging.configure(onMessage: (Map message) { - debugPrint("onMessage in fireApp (isLoaded: ${store.state.isLoaded}): $message"); + debugPrint( + "onMessage in fireApp (isLoaded: ${store.state.isLoaded}): $message"); _showItemDialog(message, _notifForMessage(message, store.state.isLoaded)); + return; }, onLaunch: (Map message) { debugPrint("onLaunch (isLoaded: ${store.state.isLoaded}): $message"); _notifForMessage(message, store.state.isLoaded); _navigateToItemDetail(message); + return; }, onResume: (Map message) { debugPrint("onResume (isLoaded: ${store.state.isLoaded}): $message"); _notifForMessage(message, store.state.isLoaded); _navigateToItemDetail(message); + return; }); _firebaseMessaging.requestNotificationPermissions( const IosNotificationSettings(sound: true, badge: true, alert: true)); @@ -140,9 +143,7 @@ class _HomePageState extends State { }); newNotifications.clear(); } - return new _ViewModel( - isLoaded: store.state.isLoaded - ); + return new _ViewModel(isLoaded: store.state.isLoaded); }, builder: (context, view) { return new Scaffold( @@ -179,45 +180,47 @@ class _HomePageState extends State { ), ), ]), - body: !view.isLoaded? new FiresSpinner(): new SafeArea( - child: Center( - child: new CenteredColumn(children: [ - new Row( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - new IconButton( - onPressed: () { - _scaffoldKey.currentState.openDrawer(); - }, - icon: new Icon(Icons.menu, - size: 30.0, color: Colors.black38)), - ]), - new Expanded( - child: new FractionallySizedBox( - alignment: FractionalOffset.center, - heightFactor: 0.7, - child: new Image.asset('images/logo-200.png', - fit: BoxFit.fitHeight))), - new Expanded( - child: new FractionallySizedBox( - alignment: FractionalOffset.topCenter, - heightFactor: 1.0, - child: new Column( + body: !view.isLoaded + ? new FiresSpinner() + : new SafeArea( + child: Center( + child: new CenteredColumn(children: [ + new Row( + mainAxisAlignment: MainAxisAlignment.start, children: [ - new Padding( - padding: const EdgeInsets.symmetric( - vertical: 10.0, horizontal: 20.0), - child: FittedBox( - child: new Text(S.of(context).appName, - maxLines: 2, - textAlign: TextAlign.center, - style: _homeFont), - fit: BoxFit.scaleDown, - )), - ], - ))) - ])), - )); + new IconButton( + onPressed: () { + _scaffoldKey.currentState.openDrawer(); + }, + icon: new Icon(Icons.menu, + size: 30.0, color: Colors.black38)), + ]), + new Expanded( + child: new FractionallySizedBox( + alignment: FractionalOffset.center, + heightFactor: 0.7, + child: new Image.asset('images/logo-200.png', + fit: BoxFit.fitHeight))), + new Expanded( + child: new FractionallySizedBox( + alignment: FractionalOffset.topCenter, + heightFactor: 1.0, + child: new Column( + children: [ + new Padding( + padding: const EdgeInsets.symmetric( + vertical: 10.0, horizontal: 20.0), + child: FittedBox( + child: new Text(S.of(context).appName, + maxLines: 2, + textAlign: TextAlign.center, + style: _homeFont), + fit: BoxFit.scaleDown, + )), + ], + ))) + ])), + )); }); } @@ -282,12 +285,13 @@ class _HomePageState extends State { // https://pub.dartlang.org/packages/firebase_messaging#-example-tab- final FirebaseMessaging _firebaseMessaging = new FirebaseMessaging(); - FireNotification _notifForMessage(Map message, bool isLoaded) { + FireNotification _notifForMessage( + Map message, bool isLoaded) { FireNotification notif; try { notif = new FireNotification( - id: new ObjectId.fromHexString(message['id']), - subsId: new ObjectId.fromHexString(message['subsId']), + id: objectIdFromJson(message['id']), + subsId: objectIdFromJson(message['subsId']), lat: double.parse(message['lat']), lon: double.parse(message['lon']), description: message['description'], @@ -299,12 +303,12 @@ class _HomePageState extends State { debugPrint(e.toString()); } if (notif != null) - // if our store is loaded, we just dispatch the notification, if not, we wait til is loaded - if (isLoaded) { - store.dispatch(new AddFireNotificationAction(notif)); - } else { - newNotifications.add(notif); - } + // if our store is loaded, we just dispatch the notification, if not, we wait til is loaded + if (isLoaded) { + store.dispatch(new AddFireNotificationAction(notif)); + } else { + newNotifications.add(notif); + } return notif; } } diff --git a/lib/layerSelectorMapPlugin.dart b/lib/layerSelectorMapPlugin.dart index d5dc01c..084c989 100644 --- a/lib/layerSelectorMapPlugin.dart +++ b/lib/layerSelectorMapPlugin.dart @@ -1,6 +1,5 @@ import 'package:comunes_flutter/comunes_flutter.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/src/widgets/framework.dart'; import 'package:flutter_map/plugin_api.dart'; import 'package:flutter_simple_dependency_injection/injector.dart'; import 'package:redux/redux.dart'; @@ -18,16 +17,19 @@ class LayerSelectorMapPluginOptions extends LayerOptions { class LayerSelectorMapPlugin extends MapPlugin { Widget LayerSelectorButton(Store store) { return new FloatingActionButton( - backgroundColor: Colors.black26, - mini: true, - child: Icon(Icons.layers, /* size: 40.0, color: Colors.black45, */ ), + backgroundColor: Colors.black26, + mini: true, + child: Icon( + Icons.layers, /* size: 40.0, color: Colors.black45, */ + ), onPressed: () { store.dispatch(new ToggleMapLayerAction()); }); } @override - Widget createLayer(LayerOptions options, MapState mapState) { + Widget createLayer( + LayerOptions options, MapState mapState, Stream stream) { Store store = Injector.getInjector().get>(); if (options is LayerSelectorMapPluginOptions) { return LayoutBuilder( diff --git a/lib/locationUtils.dart b/lib/locationUtils.dart index 2f3b55a..29fc522 100644 --- a/lib/locationUtils.dart +++ b/lib/locationUtils.dart @@ -1,29 +1,32 @@ -import 'package:location/location.dart'; -import 'package:flutter/services.dart'; import 'dart:async'; + import 'package:fires_flutter/models/yourLocation.dart'; import 'package:flutter/material.dart'; -import 'package:geocoder/geocoder.dart'; -import 'generated/i18n.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_simple_dependency_injection/injector.dart'; +import 'package:geocoder/geocoder.dart'; +import 'package:location/location.dart'; + +import 'generated/i18n.dart'; Future getUserLocation( GlobalKey scaffoldKey) async { // Platform messages may fail, so we use a try/catch PlatformException. try { Location _location = new Location(); - Map location = await _location.getLocation; + + LocationData location = await _location.getLocation(); // It seems that the lib fails with lat/lon values - var yl = new YourLocation( - lat: location['latitude'], lon: location['longitude']); + var yl = new YourLocation(lat: location.latitude, lon: location.longitude); var address; try { address = await getReverseLocation(lat: yl.lat, lon: yl.lon); yl.description = address; } catch (e) { try { - address = await getReverseLocation(lat: yl.lat, lon: yl.lon, external: true); + address = + await getReverseLocation(lat: yl.lat, lon: yl.lon, external: true); yl.description = address; } catch (e) { print('We cannot reverse geolocate'); @@ -32,22 +35,25 @@ Future getUserLocation( return yl; } on PlatformException catch (e) { if (e.code == 'PERMISSION_DENIED') { - scaffoldKey.currentState.showSnackBar(new SnackBar( + ScaffoldMessenger.of(scaffoldKey.currentContext) + .showSnackBar(new SnackBar( content: new Text(S.of(scaffoldKey.currentContext).notPermsUbication), )); } else if (e.code == 'PERMISSION_DENIED_NEVER_ASK') {} - scaffoldKey.currentState.showSnackBar(new SnackBar( - content: new Text(S.of(scaffoldKey.currentContext).isYourUbicationEnabled - ), + ScaffoldMessenger.of(scaffoldKey.currentContext).showSnackBar(new SnackBar( + content: + new Text(S.of(scaffoldKey.currentContext).isYourUbicationEnabled), )); return YourLocation.noLocation; } } -Future getReverseLocation({@required lat, @required lon, - bool external = false}) async { +Future getReverseLocation( + {@required lat, @required lon, bool external = false}) async { final coordinates = new Coordinates(lat, lon); - var geoCoder = external ? Geocoder.google(Injector.getInjector().get(key: "gmapKey")) : Geocoder.local; + var geoCoder = external + ? Geocoder.google(Injector.getInjector().get(key: "gmapKey")) + : Geocoder.local; var addresses = await geoCoder.findAddressesFromCoordinates(coordinates); var first = addresses.first; print("${first.featureName} : ${first.addressLine}"); diff --git a/lib/mainCommon.dart b/lib/mainCommon.dart index eaf76e2..3b6876e 100644 --- a/lib/mainCommon.dart +++ b/lib/mainCommon.dart @@ -3,6 +3,7 @@ import 'dart:async'; import 'package:comunes_flutter/comunes_flutter.dart'; import 'package:flutter/material.dart'; import 'package:flutter_simple_dependency_injection/injector.dart'; +import 'package:package_info/package_info.dart'; import 'package:redux/redux.dart'; import 'package:sentry/sentry.dart'; @@ -12,7 +13,6 @@ import 'models/appState.dart'; import 'models/firesApi.dart'; import 'redux/fetchDataMiddleware.dart'; import 'redux/reducers.dart'; -import 'package:package_info/package_info.dart'; import 'sentryReport.dart'; Future loadPackageInfo() async { @@ -22,13 +22,14 @@ Future loadPackageInfo() async { Future> loadSecrets() async { return await SecretLoader( - secretPath: globals.isDevelopment? - 'assets/private-settings-dev.json' : - 'assets/private-settings.json' - ).load(); + secretPath: globals.isDevelopment + ? 'assets/private-settings-dev.json' + : 'assets/private-settings.json') + .load(); } void mainCommon(List> otherMiddleware) { + WidgetsFlutterBinding.ensureInitialized(); final injector = Injector.getInjector(); injector.map((i) => new FiresApi(), isSingleton: true); loadPackageInfo().then((packageInfo) { @@ -36,13 +37,12 @@ void mainCommon(List> otherMiddleware) { print('Running version ${packageInfo.version}'); loadSecrets().then((secrets) { final store = new Store(appStateReducer, - initialState: new AppState( - gmapKey: secrets['gmapKey'], - firesApiKey: secrets['firesApiKey'], - serverUrl: secrets['firesApiUrl'], - firesApiUrl: secrets['firesApiUrl'] + "api/v1/"), - middleware: List.from(otherMiddleware) - ..add(fetchDataMiddleware)); + initialState: new AppState( + gmapKey: secrets['gmapKey'], + firesApiKey: secrets['firesApiKey'], + serverUrl: secrets['firesApiUrl'], + firesApiUrl: secrets['firesApiUrl'] + "api/v1/"), + middleware: List.from(otherMiddleware)..add(fetchDataMiddleware)); injector.map>((i) => store); injector.map((i) => store.state.firesApiUrl, key: "firesApiUrl"); @@ -56,9 +56,9 @@ void mainCommon(List> otherMiddleware) { } // https://flutter.io/cookbook/maintenance/error-reporting/ - runZoned>(() async { + runZonedGuarded>(() async { runApp(new FiresApp(store)); - }, onError: (error, stackTrace) { + }, (Object error, StackTrace stackTrace) { // Whenever an error occurs, call the `_reportError` function. This will send // Dart errors to our dev console or Sentry depending on the environment. reportError(error, stackTrace); diff --git a/lib/mainDev.dart b/lib/mainDev.dart index 2140c54..a3c155b 100644 --- a/lib/mainDev.dart +++ b/lib/mainDev.dart @@ -30,7 +30,7 @@ LoggingMiddleware customLogPrinter({ void main() { globals.isDevelopment = true; - + print("Is development!"); String onlyLogActionFormatter( State state, dynamic action, @@ -39,9 +39,9 @@ void main() { return ">>>>> ${action.toString().replaceAll('Instance of ', '')}"; } - LogLevel logRedux = LogLevel.none; + LogLevel logRedux = LogLevel.actions; - List devMiddlewares = logRedux == LogLevel.none + List devMiddlewares = logRedux == LogLevel.actions ? [] : [ customLogPrinter( diff --git a/lib/mainDrawer.dart b/lib/mainDrawer.dart index 447ee59..da2cd21 100644 --- a/lib/mainDrawer.dart +++ b/lib/mainDrawer.dart @@ -1,4 +1,4 @@ -import 'package:badge/badge.dart'; +import 'package:badges/badges.dart'; import 'package:comunes_flutter/comunes_flutter.dart'; import 'package:flutter/material.dart'; import 'package:flutter_redux/flutter_redux.dart'; @@ -97,13 +97,28 @@ Widget mainDrawer(BuildContext context, String currentRoute) { new ListTile( leading: const Icon(Icons.notifications), selected: currentRoute == FireNotificationList.routeName, - title: view.unreadCount > 0 - ? Badge.after( - spacing: 5.0, - borderColor: Colors.red, - child: Text(S.of(context).fireNotificationsTitleShort), - value: ' ${view.unreadCount.toString()} ') - : Text(S.of(context).fireNotificationsTitleShort), + title: Text(S.of(context).fireNotificationsTitleShort), + trailing: SizedBox( + width: 100, + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Badge( + position: BadgePosition(top: 0, end: 0), + padding: EdgeInsets.all(7), + badgeColor: view.unreadCount > 0 + ? Colors.red + : Colors.grey, + // spacing: 5.0, + // borderColor: Colors.red, + //child: Text(S.of(context).fireNotificationsTitleShort), + badgeContent: Text( + '${view.unreadCount.toString()}', + style: TextStyle(color: Colors.white), + )) + ])), + + // Text(S.of(context).fireNotificationsTitleShort), onTap: () { Navigator.popAndPushNamed( context, FireNotificationList.routeName); diff --git a/lib/models/appState.dart b/lib/models/appState.dart index f1cc365..679eb62 100644 --- a/lib/models/appState.dart +++ b/lib/models/appState.dart @@ -5,21 +5,21 @@ import 'package:connectivity/connectivity.dart'; import 'package:fires_flutter/models/fireNotification.dart'; import 'package:fires_flutter/models/yourLocation.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_map/flutter_map.dart'; import 'package:json_annotation/json_annotation.dart'; +import 'package:latlong/latlong.dart'; import 'package:meta/meta.dart'; import 'fireMapState.dart'; import 'user.dart'; export 'fireMapState.dart'; -import 'package:flutter_map/flutter_map.dart'; -import 'package:latlong/latlong.dart'; part 'appState.g.dart'; @immutable @JsonSerializable(nullable: false) -class AppState extends Object with _$AppStateSerializerMixin { +class AppState { @JsonKey(ignore: true) final bool isLoading; @JsonKey(ignore: true) @@ -58,7 +58,7 @@ class AppState extends Object with _$AppStateSerializerMixin { this.user: const User.initial(), this.isLoading: false, this.isLoaded: false, - this.error: null, + this.error, this.gmapKey, this.firesApiKey, this.firesApiUrl, @@ -102,11 +102,7 @@ class AppState extends Object with _$AppStateSerializerMixin { @override String toString() { - return 'AppState{\nuser: ${user}\nconnectivity: ${connectivity - .toString()}\nisLoading: $isLoading\nisLoaded: $isLoaded\napiKey: ${ellipse( - firesApiKey, - 8)}\napiUrl: ${firesApiUrl}\nserverUrl: ${serverUrl}\nfireMapState: $fireMapState\nyourLocations count: ${yourLocations - .length}\nunread notif: ${fireNotificationsUnread}\nfireNotifications: ${fireNotifications}\nyourLocations: ${yourLocations}}'; + return 'AppState{\nuser: $user\nconnectivity: ${connectivity.toString()}\nisLoading: $isLoading\nisLoaded: $isLoaded\napiKey: ${ellipse(firesApiKey, 8)}\napiUrl: $firesApiUrl\nserverUrl: $serverUrl\nfireMapState: $fireMapState\nyourLocations count: ${yourLocations.length}\nunread notif: $fireNotificationsUnread\nfireNotifications: $fireNotifications\nyourLocations: $yourLocations}'; } } diff --git a/lib/models/appState.g.dart b/lib/models/appState.g.dart index 49bd41e..4a42177 100644 --- a/lib/models/appState.g.dart +++ b/lib/models/appState.g.dart @@ -1,5 +1,3 @@ -// Copyright (c) 2018, Comunes Association. - // GENERATED CODE - DO NOT MODIFY BY HAND part of 'appState.dart'; @@ -8,37 +6,30 @@ part of 'appState.dart'; // JsonSerializableGenerator // ************************************************************************** -AppState _$AppStateFromJson(Map json) => new AppState( - yourLocations: (json['yourLocations'] as List) - .map((e) => new YourLocation.fromJson(e as Map)) - .toList(), - fireNotifications: (json['fireNotifications'] as List) - .map((e) => new FireNotification.fromJson(e as Map)) - .toList()); - -abstract class _$AppStateSerializerMixin { - List get yourLocations; - List get fireNotifications; - Map toJson() => new _$AppStateJsonMapWrapper(this); +AppState _$AppStateFromJson(Map json) { + return $checkedNew('AppState', json, () { + final val = AppState( + yourLocations: $checkedConvert( + json, + 'yourLocations', + (v) => (v as List) + .map((e) => + YourLocation.fromJson(Map.from(e as Map))) + .toList()), + fireNotifications: $checkedConvert( + json, + 'fireNotifications', + (v) => (v as List) + .map((e) => FireNotification.fromJson( + Map.from(e as Map))) + .toList()), + ); + return val; + }); } -class _$AppStateJsonMapWrapper extends $JsonMapWrapper { - final _$AppStateSerializerMixin _v; - _$AppStateJsonMapWrapper(this._v); - - @override - Iterable get keys => const ['yourLocations', 'fireNotifications']; - - @override - dynamic operator [](Object key) { - if (key is String) { - switch (key) { - case 'yourLocations': - return _v.yourLocations; - case 'fireNotifications': - return _v.fireNotifications; - } - } - return null; - } -} +Map _$AppStateToJson(AppState instance) => { + 'yourLocations': instance.yourLocations.map((e) => e.toJson()).toList(), + 'fireNotifications': + instance.fireNotifications.map((e) => e.toJson()).toList(), + }; diff --git a/lib/models/fireNotification.dart b/lib/models/fireNotification.dart index 5c3cef4..72c0de9 100644 --- a/lib/models/fireNotification.dart +++ b/lib/models/fireNotification.dart @@ -1,13 +1,14 @@ -import 'package:bson_objectid/bson_objectid.dart'; import 'package:comunes_flutter/comunes_flutter.dart'; import 'package:flutter/material.dart'; import 'package:json_annotation/json_annotation.dart'; +import 'package:objectid/objectid.dart'; + import '../objectIdUtils.dart'; part 'fireNotification.g.dart'; @JsonSerializable(nullable: false) -class FireNotification extends Object with _$FireNotificationSerializerMixin { +class FireNotification { @JsonKey(toJson: objectIdToJson, fromJson: objectIdFromJson) ObjectId id; final double lat; @@ -74,11 +75,11 @@ class FireNotification extends Object with _$FireNotificationSerializerMixin { @override String toString() { - return 'FireNotification {id: $id, lat: $lat, lon: $lon, when: $when, read: $read, subsId: $subsId, sealed ${ellipse( - sealed)}'; + return 'FireNotification {id: $id, lat: $lat, lon: $lon, when: $when, read: $read, subsId: $subsId, sealed ${ellipse(sealed)}'; } static final Map> routes = >{}; + Map toJson() => _$FireNotificationToJson(this); /* Route getRoute(Store store) { diff --git a/lib/models/fireNotification.g.dart b/lib/models/fireNotification.g.dart index 30afb18..b29fc1f 100644 --- a/lib/models/fireNotification.g.dart +++ b/lib/models/fireNotification.g.dart @@ -1,5 +1,3 @@ -// Copyright (c) 2018, Comunes Association. - // GENERATED CODE - DO NOT MODIFY BY HAND part of 'fireNotification.dart'; @@ -8,67 +6,31 @@ part of 'fireNotification.dart'; // JsonSerializableGenerator // ************************************************************************** -FireNotification _$FireNotificationFromJson(Map json) => - new FireNotification( - id: objectIdFromJson(json['id'] as String), - lat: (json['lat'] as num).toDouble(), - lon: (json['lon'] as num).toDouble(), - description: json['description'] as String, - when: DateTime.parse(json['when'] as String), - read: json['read'] as bool, - sealed: json['sealed'] as String, - subsId: objectIdFromJson(json['subsId'] as String)); - -abstract class _$FireNotificationSerializerMixin { - ObjectId get id; - double get lat; - double get lon; - String get description; - DateTime get when; - String get sealed; - ObjectId get subsId; - bool get read; - Map toJson() => new _$FireNotificationJsonMapWrapper(this); +FireNotification _$FireNotificationFromJson(Map json) { + return $checkedNew('FireNotification', json, () { + final val = FireNotification( + id: $checkedConvert(json, 'id', (v) => objectIdFromJson(v as String)), + lat: $checkedConvert(json, 'lat', (v) => (v as num).toDouble()), + lon: $checkedConvert(json, 'lon', (v) => (v as num).toDouble()), + description: $checkedConvert(json, 'description', (v) => v as String), + when: $checkedConvert(json, 'when', (v) => DateTime.parse(v as String)), + read: $checkedConvert(json, 'read', (v) => v as bool), + sealed: $checkedConvert(json, 'sealed', (v) => v as String), + subsId: + $checkedConvert(json, 'subsId', (v) => objectIdFromJson(v as String)), + ); + return val; + }); } -class _$FireNotificationJsonMapWrapper extends $JsonMapWrapper { - final _$FireNotificationSerializerMixin _v; - _$FireNotificationJsonMapWrapper(this._v); - - @override - Iterable get keys => const [ - 'id', - 'lat', - 'lon', - 'description', - 'when', - 'sealed', - 'subsId', - 'read' - ]; - - @override - dynamic operator [](Object key) { - if (key is String) { - switch (key) { - case 'id': - return objectIdToJson(_v.id); - case 'lat': - return _v.lat; - case 'lon': - return _v.lon; - case 'description': - return _v.description; - case 'when': - return _v.when.toIso8601String(); - case 'sealed': - return _v.sealed; - case 'subsId': - return objectIdToJson(_v.subsId); - case 'read': - return _v.read; - } - } - return null; - } -} +Map _$FireNotificationToJson(FireNotification instance) => + { + 'id': objectIdToJson(instance.id), + 'lat': instance.lat, + 'lon': instance.lon, + 'description': instance.description, + 'when': instance.when.toIso8601String(), + 'sealed': instance.sealed, + 'subsId': objectIdToJson(instance.subsId), + 'read': instance.read, + }; diff --git a/lib/models/fireNotificationsPersist.dart b/lib/models/fireNotificationsPersist.dart index 477dbe3..82ac90d 100644 --- a/lib/models/fireNotificationsPersist.dart +++ b/lib/models/fireNotificationsPersist.dart @@ -1,8 +1,9 @@ import 'dart:async'; import 'dart:convert'; -import 'package:fires_flutter/models/fireNotification.dart'; import 'package:comunes_flutter/comunes_flutter.dart'; +import 'package:fires_flutter/models/fireNotification.dart'; + import '../globals.dart' as globals; final String fireNotificationKey = 'fireNotifications'; @@ -10,7 +11,7 @@ final String fireNotificationKey = 'fireNotifications'; Future> loadFireNotifications() async { return await globals.prefs.then((prefs) { List FireNotifications = prefs.getStringList(fireNotificationKey); - List persistedList = List(); + List persistedList = []; if (FireNotifications == null) { FireNotifications = []; // first run, init with empty list diff --git a/lib/models/firesApi.dart b/lib/models/firesApi.dart index a689bbc..ac2b0b5 100644 --- a/lib/models/firesApi.dart +++ b/lib/models/firesApi.dart @@ -1,26 +1,26 @@ import 'dart:async'; import 'dart:convert'; -import 'package:flutter_map/flutter_map.dart'; -import 'package:flutter/material.dart'; -import 'falsePositiveTypes.dart'; -import 'package:bson_objectid/bson_objectid.dart'; import 'package:fires_flutter/models/yourLocation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_map/flutter_map.dart'; import 'package:http/http.dart' as ht; import 'package:jaguar_resty/jaguar_resty.dart' as resty; import 'package:latlong/latlong.dart'; import '../globals.dart' as globals; +import '../objectIdUtils.dart'; import '../redux/actions.dart'; import 'appState.dart'; +import 'falsePositiveTypes.dart'; class FiresApi { FiresApi() { resty.globalClient = new ht.IOClient(); } - Future createUser(AppState state, String mobileToken, - String lang) async { + Future createUser( + AppState state, String mobileToken, String lang) async { assert(state.firesApiUrl != null); assert(state.firesApiKey != null); assert(mobileToken != null); @@ -38,6 +38,8 @@ class FiresApi { if (response.statusCode == 200) { // print(response.body); return json.decode(response.body)['data']['userId']; + } else { + return throw "Unexpected error on create user"; } }); } @@ -45,28 +47,29 @@ class FiresApi { Future> fetchYourLocations(AppState state) async { final apiKey = state.firesApiKey; final mobileToken = state.user.token; - final String url = '${state - .firesApiUrl}mobile/subscriptions/all/$apiKey/$mobileToken'; + final String url = + '${state.firesApiUrl}mobile/subscriptions/all/$apiKey/$mobileToken'; // if (globals.isDevelopment) print('$url'); return await resty.get(url).go().then((response) { if (response.statusCode == 200) { // if (globals.isDevelopment) print(response.body); final dataSubscriptions = - json.decode(response.body)['data']['subscriptions']; + json.decode(response.body)['data']['subscriptions']; List subscribed = []; for (int i = 0; i < dataSubscriptions.length; i++) { var el = dataSubscriptions[i]; var lat = el['location']['lat']; var lon = el['location']['lon']; subscribed.add(new YourLocation( - id: ObjectId.fromHexString(el['_id']['_str']), - lat: lat, - lon: lon, - subscribed: true, - distance: el['distance'])); + id: objectIdFromJson(el['_id']['_str']), + lat: lat, + lon: lon, + subscribed: true, + distance: el['distance'])); } - return subscribed; + } else { + return throw "Unexpected error fetching your locations"; } }); } @@ -83,13 +86,12 @@ class FiresApi { final params = { "token": state.firesApiKey, "mobileToken": state.user.token, - "id": loc.id.toHexString(), + "id": loc.id.hexString, "lat": loc.lat, "lon": loc.lon, "distance": loc.distance }; - final String url = '${state - .firesApiUrl}mobile/subscriptions'; + final String url = '${state.firesApiUrl}mobile/subscriptions'; return await resty.post(url).json(params).go().then((response) { if (response.statusCode == 200) { // print(response.body); @@ -97,6 +99,7 @@ class FiresApi { } else { // take care of? "Unexpected error in REST call: Error: The user is already subscribed to this area [on-already-subscribed]" print(response.body); + return throw "Unexpected error on subscribe"; } }); } @@ -108,22 +111,24 @@ class FiresApi { final apiKey = state.firesApiKey; final mobileToken = state.user.token; assert(subsId != null); - final String url = '${state - .firesApiUrl}mobile/subscriptions/$apiKey/$mobileToken/$subsId'; + final String url = + '${state.firesApiUrl}mobile/subscriptions/$apiKey/$mobileToken/$subsId'; return await resty.delete(url).go().then((response) { if (response.statusCode == 200) { // print(response.body); return true; + } else { + return throw "Unexpected error on unsubscribe"; } }); } Future getFiresInLocation( - {AppState state, double lat, double lon, int distance}) async { + {AppState state, double lat, double lon, int distance}) async { assert(state.firesApiUrl != null); assert(state.firesApiKey != null); - var url = '${state.firesApiUrl}fires-in-full/${state - .firesApiKey}/${lat}/${lon}/${distance}'; + var url = + '${state.firesApiUrl}fires-in-full/${state.firesApiKey}/$lat/$lon/$distance'; if (globals.isDevelopment) print(url); return await resty.get(url).go().then((response) { if (response.statusCode == 200) { @@ -138,13 +143,13 @@ class FiresApi { var industriesCount = industries.length; var falsePosCount = falsePos.length; print( - '(Pos: $lat, $lon) real: $numFires, fire: $firesCount falsePos: $falsePosCount industries: $industriesCount'); + '(Pos: $lat, $lon) real: $numFires, fire: $firesCount falsePos: $falsePosCount industries: $industriesCount'); } return new UpdateFireMapStatsAction( - numFires: numFires, - fires: fires, - falsePos: falsePos, - industries: industries); + numFires: numFires, + fires: fires, + falsePos: falsePos, + industries: industries); } else throw Exception('Wrong response trying to get fire data'); }); @@ -153,8 +158,8 @@ class FiresApi { Future> getMonitoredAreas({AppState state}) async { assert(state.firesApiUrl != null); assert(state.firesApiKey != null); - var url = '${state.firesApiUrl}status/subs-public-union/${state - .firesApiKey}'; + var url = + '${state.firesApiUrl}status/subs-public-union/${state.firesApiKey}'; var color = const Color(0xFF145A32); return await resty.get(url).go().then((response) { if (response.statusCode == 200) { @@ -162,8 +167,8 @@ class FiresApi { // print(resultDecoded['data']['union']); List union = []; final multipolygon = - json.decode(resultDecoded['data']['union']['value'])['geometry'] - ['coordinates']; + json.decode(resultDecoded['data']['union']['value'])['geometry'] + ['coordinates']; for (List polygon in multipolygon) { for (List hole in polygon) { List points = []; @@ -171,7 +176,7 @@ class FiresApi { points.add(new LatLng(point[1].toDouble(), point[0].toDouble())); } union.add( - new Polyline(points: points, color: color, strokeWidth: 3.0)); + new Polyline(points: points, color: color, strokeWidth: 3.0)); } } return union; @@ -181,7 +186,7 @@ class FiresApi { } Future markFalsePositive(AppState state, String mobileToken, - String sealed, FalsePositiveType type) async { + String sealed, FalsePositiveType type) async { assert(state.firesApiUrl != null); assert(state.firesApiKey != null); assert(mobileToken != null); @@ -198,8 +203,8 @@ class FiresApi { return await resty.post(url).json(params).go().then((response) { if (response.statusCode == 200) { // print(response.body); - if (globals.isDevelopment) print( - json.decode(response.body)['data']['upsert']); + if (globals.isDevelopment) + print(json.decode(response.body)['data']['upsert']); return true; } else { debugPrint(json.decode(response.body)); diff --git a/lib/models/yourLocation.dart b/lib/models/yourLocation.dart index b0b2472..fb64502 100644 --- a/lib/models/yourLocation.dart +++ b/lib/models/yourLocation.dart @@ -1,14 +1,12 @@ -import 'package:bson_objectid/bson_objectid.dart'; +import 'package:fires_flutter/objectIdUtils.dart'; import 'package:flutter/material.dart'; import 'package:json_annotation/json_annotation.dart'; -import 'package:fires_flutter/objectIdUtils.dart'; +import 'package:objectid/objectid.dart'; part 'yourLocation.g.dart'; - - @JsonSerializable(nullable: false) -class YourLocation extends Object with _$YourLocationSerializerMixin { +class YourLocation { @JsonKey(toJson: objectIdToJson, fromJson: objectIdFromJson) ObjectId id; final double lat; @@ -22,40 +20,33 @@ class YourLocation extends Object with _$YourLocationSerializerMixin { _$YourLocationFromJson(json); static YourLocation noLocation = new YourLocation(lat: 0.0, lon: 0.0); -static const int withoutStats = null; + static const int withoutStats = null; YourLocation( {this.id, @required this.lat, @required this.lon, this.description, this.distance: 10, - int currentNumFires: withoutStats, + int currentNumFires: withoutStats, this.subscribed: false}) { if (this.description == null) this.description = 'Position: ${this.lat}, ${this.lon}'; if (this.id == null) this.id = new ObjectId(); } + Map toJson() => _$YourLocationToJson(this); YourLocation copyWith( - {id, - lat, - lon, - description, - distance, - currentNumFires, - subscribed}) { + {id, lat, lon, description, distance, currentNumFires, subscribed}) { return new YourLocation( - id: id?? this.id, - lat: lat?? this.lat, - lon: lon?? this.lon, - description: description?? this.description, - distance: distance?? this.distance, - currentNumFires: currentNumFires ?? this.currentNumFires, - subscribed: subscribed?? this.subscribed - ); + id: id ?? this.id, + lat: lat ?? this.lat, + lon: lon ?? this.lon, + description: description ?? this.description, + distance: distance ?? this.distance, + currentNumFires: currentNumFires ?? this.currentNumFires, + subscribed: subscribed ?? this.subscribed); } - @override bool operator ==(Object other) => identical(this, other) || @@ -66,7 +57,7 @@ static const int withoutStats = null; lon == other.lon && description == other.description && subscribed == other.subscribed && - currentNumFires == other.currentNumFires && + currentNumFires == other.currentNumFires && distance == other.distance; @override diff --git a/lib/models/yourLocation.g.dart b/lib/models/yourLocation.g.dart index 5313b32..4261219 100644 --- a/lib/models/yourLocation.g.dart +++ b/lib/models/yourLocation.g.dart @@ -1,5 +1,3 @@ -// Copyright (c) 2018, Comunes Association. - // GENERATED CODE - DO NOT MODIFY BY HAND part of 'yourLocation.dart'; @@ -8,51 +6,29 @@ part of 'yourLocation.dart'; // JsonSerializableGenerator // ************************************************************************** -YourLocation _$YourLocationFromJson(Map json) => - new YourLocation( - id: objectIdFromJson(json['id'] as String), - lat: (json['lat'] as num).toDouble(), - lon: (json['lon'] as num).toDouble(), - description: json['description'] as String, - distance: json['distance'] as int, - subscribed: json['subscribed'] as bool); - -abstract class _$YourLocationSerializerMixin { - ObjectId get id; - double get lat; - double get lon; - String get description; - bool get subscribed; - int get distance; - Map toJson() => new _$YourLocationJsonMapWrapper(this); +YourLocation _$YourLocationFromJson(Map json) { + return $checkedNew('YourLocation', json, () { + final val = YourLocation( + id: $checkedConvert(json, 'id', (v) => objectIdFromJson(v as String)), + lat: $checkedConvert(json, 'lat', (v) => (v as num).toDouble()), + lon: $checkedConvert(json, 'lon', (v) => (v as num).toDouble()), + description: $checkedConvert(json, 'description', (v) => v as String), + distance: $checkedConvert(json, 'distance', (v) => v as int), + currentNumFires: + $checkedConvert(json, 'currentNumFires', (v) => v as int), + subscribed: $checkedConvert(json, 'subscribed', (v) => v as bool), + ); + return val; + }); } -class _$YourLocationJsonMapWrapper extends $JsonMapWrapper { - final _$YourLocationSerializerMixin _v; - _$YourLocationJsonMapWrapper(this._v); - - @override - Iterable get keys => - const ['id', 'lat', 'lon', 'description', 'subscribed', 'distance']; - - @override - dynamic operator [](Object key) { - if (key is String) { - switch (key) { - case 'id': - return objectIdToJson(_v.id); - case 'lat': - return _v.lat; - case 'lon': - return _v.lon; - case 'description': - return _v.description; - case 'subscribed': - return _v.subscribed; - case 'distance': - return _v.distance; - } - } - return null; - } -} +Map _$YourLocationToJson(YourLocation instance) => + { + 'id': objectIdToJson(instance.id), + 'lat': instance.lat, + 'lon': instance.lon, + 'description': instance.description, + 'subscribed': instance.subscribed, + 'distance': instance.distance, + 'currentNumFires': instance.currentNumFires, + }; diff --git a/lib/models/yourLocationPersist.dart b/lib/models/yourLocationPersist.dart index c992974..8b0d2f7 100644 --- a/lib/models/yourLocationPersist.dart +++ b/lib/models/yourLocationPersist.dart @@ -1,8 +1,9 @@ import 'dart:async'; import 'dart:convert'; -import 'package:fires_flutter/models/yourLocation.dart'; import 'package:comunes_flutter/comunes_flutter.dart'; +import 'package:fires_flutter/models/yourLocation.dart'; + import '../globals.dart' as globals; final String locationKey = 'yourlocations'; @@ -10,7 +11,7 @@ final String locationKey = 'yourlocations'; Future> loadYourLocations() async { return await globals.prefs.then((prefs) { List yourLocations = prefs.getStringList(locationKey); - List persistedList = List(); + List persistedList = []; if (yourLocations == null) { yourLocations = []; // first run, init with empty list diff --git a/lib/objectIdUtils.dart b/lib/objectIdUtils.dart index 89d398f..8e80b6e 100644 --- a/lib/objectIdUtils.dart +++ b/lib/objectIdUtils.dart @@ -1,9 +1,10 @@ -import 'package:bson_objectid/bson_objectid.dart'; +import 'package:objectid/objectid.dart'; objectIdFromJson(String json) { - return new ObjectId.fromHexString(json); + return new ObjectId.fromHexString( + json.replaceFirst("ObjectId(", "").replaceAll(")", "")); } objectIdToJson(ObjectId o) { return o.toString(); -} \ No newline at end of file +} diff --git a/lib/placesAutocompleteUtils.dart b/lib/placesAutocompleteUtils.dart index 8e83dcb..ba03d5d 100644 --- a/lib/placesAutocompleteUtils.dart +++ b/lib/placesAutocompleteUtils.dart @@ -1,12 +1,12 @@ import 'dart:async'; +import 'package:fires_flutter/models/yourLocation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_google_places_autocomplete/flutter_google_places_autocomplete.dart'; - -import 'package:fires_flutter/models/yourLocation.dart'; -import 'generated/i18n.dart'; import 'package:flutter_simple_dependency_injection/injector.dart'; +import 'generated/i18n.dart'; + Future openPlacesDialog(GlobalKey sc) async { Mode _mode = Mode.overlay; String gmapKey = Injector.getInjector().get(key: "gmapKey"); @@ -16,7 +16,7 @@ Future openPlacesDialog(GlobalKey sc) async { hint: S.of(sc.currentContext).typeTheNameOfAPlace, apiKey: gmapKey, onError: (res) { - sc.currentState + ScaffoldMessenger.of(sc.currentContext) .showSnackBar(new SnackBar(content: new Text(res.errorMessage))); print('Error $res'); }, diff --git a/lib/redux/fetchDataMiddleware.dart b/lib/redux/fetchDataMiddleware.dart index 4b419c6..fe9b47f 100644 --- a/lib/redux/fetchDataMiddleware.dart +++ b/lib/redux/fetchDataMiddleware.dart @@ -1,16 +1,17 @@ import 'dart:async'; -import 'package:bson_objectid/bson_objectid.dart'; import 'package:fires_flutter/models/fireNotification.dart'; import 'package:fires_flutter/models/yourLocation.dart'; import 'package:flutter_simple_dependency_injection/injector.dart'; import 'package:just_debounce_it/just_debounce_it.dart'; +import 'package:objectid/objectid.dart'; import 'package:redux/redux.dart'; import '../models/appState.dart'; import '../models/fireNotificationsPersist.dart'; import '../models/firesApi.dart'; import '../models/yourLocationPersist.dart'; +import '../objectIdUtils.dart'; import 'actions.dart'; // A middleware takes in 3 parameters: your Store, which you can use to @@ -147,16 +148,18 @@ void fetchDataMiddleware(Store store, action, NextDispatcher next) { .then((List subscribedLocations) { // If it succeeds, dispatch a success action with the YourLocations. // Our reducer will then update the State using these YourLocations. - // print('Subscribed to: ${subscribedLocations.length}'); + print('Subscribed to: ${subscribedLocations.length}'); if (subscribedLocations is List) { // unsubscribe all locally to sync the subs state localLocations.forEach((location) => location.subscribed = false); - // print('Local persisted: ${localLocations.length}'); + print('Local persisted: ${localLocations.length}'); subscribedLocations.forEach((subsLoc) { - localLocations.firstWhere( + var locSubs = localLocations.firstWhere( (localLocation) => localLocation.id == subsLoc.id, orElse: () { localLocations.add(subsLoc); - }).subscribed = true; + return subsLoc; + }); + locSubs.subscribed = true; }); } @@ -251,7 +254,7 @@ void getFiresStatsInFire(Store store, FireNotification notif) { } void unsubsViaApi(Store store, ObjectId id, onUnsubs) { - api.unsubscribe(store.state, id.toHexString()).then((res) { + api.unsubscribe(store.state, id.hexString).then((res) { onUnsubs(); persistYourLocations(store.state.yourLocations); }); @@ -260,9 +263,9 @@ void unsubsViaApi(Store store, ObjectId id, onUnsubs) { void subscribeViaApi(Store store, YourLocation loc, onSubs) { api.subscribe(store.state, loc).then((subsId) { YourLocation sub = loc; - if (loc.id != subsId) { - sub.id = new ObjectId.fromHexString(subsId); - } + // if (loc.id != subsId) { + sub.id = objectIdFromJson(subsId); + // } onSubs(sub); persistYourLocations(store.state.yourLocations); }); diff --git a/lib/redux/fireNotificationReducer.dart b/lib/redux/fireNotificationReducer.dart index a3cd083..3e62815 100644 --- a/lib/redux/fireNotificationReducer.dart +++ b/lib/redux/fireNotificationReducer.dart @@ -28,7 +28,8 @@ List _deletedFireNotification( } List _updatedFireNotification( - List notifications, UpdatedFireNotificationAction action) { + List notifications, + UpdatedFireNotificationAction action) { return notifications .map((notif) => notif.id == action.notif.id ? action.notif : notif) .toList(); @@ -45,5 +46,5 @@ List _readedFireNotification( List _deletedAllFireNotifications( List notifications, DeletedAllFireNotificationAction action) { - return new List(); + return []; } diff --git a/lib/redux/yourLocationActions.dart b/lib/redux/yourLocationActions.dart index 0fec9b7..adcd573 100644 --- a/lib/redux/yourLocationActions.dart +++ b/lib/redux/yourLocationActions.dart @@ -1,5 +1,5 @@ -import 'package:bson_objectid/bson_objectid.dart'; import 'package:fires_flutter/models/yourLocation.dart'; +import 'package:objectid/objectid.dart'; abstract class YourLocationActions {} diff --git a/lib/theme.dart b/lib/theme.dart index 3f91ff8..99c9bd8 100644 --- a/lib/theme.dart +++ b/lib/theme.dart @@ -12,7 +12,7 @@ ThemeData _buildFiresTheme() { buttonColor: fires900, scaffoldBackgroundColor: firesSurfaceWhite, cardColor: firesBackgroundWhite, - textSelectionColor: fires300, + textSelectionTheme: TextSelectionThemeData(selectionColor: fires300), errorColor: firesErrorRed, //TODO: Add the text themes (103) diff --git a/lib/themeDev.dart b/lib/themeDev.dart index 025dfba..59aea06 100644 --- a/lib/themeDev.dart +++ b/lib/themeDev.dart @@ -12,7 +12,7 @@ ThemeData _buildFiresTheme() { buttonColor: fires900, scaffoldBackgroundColor: firesSurfaceWhite, cardColor: firesBackgroundWhite, - textSelectionColor: fires300, + textSelectionTheme: TextSelectionThemeData(selectionColor: fires300), errorColor: firesErrorRed, //TODO: Add the text themes (103) diff --git a/lib/zoomMapPlugin.dart b/lib/zoomMapPlugin.dart index fd29515..6f3208a 100644 --- a/lib/zoomMapPlugin.dart +++ b/lib/zoomMapPlugin.dart @@ -1,7 +1,6 @@ -import 'package:flutter/src/widgets/framework.dart'; -import 'package:flutter_map/plugin_api.dart'; -import 'package:flutter/material.dart'; import 'package:comunes_flutter/comunes_flutter.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_map/plugin_api.dart'; class ZoomMapPluginOptions extends LayerOptions { final String text; @@ -22,7 +21,8 @@ class ZoomMapPlugin extends MapPlugin { } @override - Widget createLayer(LayerOptions options, MapState mapState) { + Widget createLayer( + LayerOptions options, MapState mapState, Stream stream) { if (options is ZoomMapPluginOptions) { /* print('point ${mapState .getPixelBounds(mapState.zoom) diff --git a/pubspec.yaml b/pubspec.yaml index 0585ce9..7c21648 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,39 +1,42 @@ name: fires_flutter description: All Against Fire +environment: + sdk: ">=2.7.0 <3.0.0" + dependencies: flutter: sdk: flutter # i18n flutter_localizations: sdk: flutter - intl: "^0.15.6" - + intl: "^0.16.1" # https://pub.dartlang.org/packages/flutter # utils - simple_moment: "^0.0.3" - just_debounce_it: "^1.0.4" + simple_moment: "^1.0.7" + just_debounce_it: "^3.0.0+2" flutter_simple_dependency_injection: "^0.0.2" sentry: "^2.0.2" - flutter_markdown: "^0.1.5" - url_launcher: "^3.0.2" - package_info: "^0.3.2" + flutter_markdown: "^0.5.1" + url_launcher: "^5.7.10" + package_info: ^0.4.3+2 launch_review: "^1.0.1" # net http: "^0.11.3+16" jaguar_resty: "^2.5.5" - connectivity: "^0.3.1" + connectivity: ^2.0.2 share: "^0.5.2" # data - json_annotation: "^0.2.3" + json_annotation: "^3.1.1" shared_preferences: "^0.4.2" - bson_objectid: "^0.1.0" + # bson_objectid: "^0.1.0 + objectid: 1.1.0 comunes_flutter: - git: git@github.com:comunes/comunes-flutter.git - # path: /home/vjrj/dev/comunes_flutter + #git: git@github.com:comunes/comunes-flutter.git + path: /home/vjrj/dev/comunes_flutter # version: "^0.0.12" # redux @@ -42,33 +45,36 @@ dependencies: redux_logging: "^0.3.0" # maps, geo, etc - flutter_map: "^0.0.10" - flutter_google_places_autocomplete: "^0.0.4" - location: "^1.3.4" - geocoder: "^0.0.1" + flutter_map: ^0.10.1+1 + flutter_google_places_autocomplete: "^0.1.3" + location: ^3.2.1 + geocoder: "^0.2.1" # layout - fluttery: "^0.0.7" + # fluttery: "^0.0.7" # https://pub.dartlang.org/packages/padder padder: "^1.0.1" flutter_fab_dialer: "^0.0.5" - badge: "^0.0.2" + # badge: "^0.0.2" + badges: ^1.1.6 flutter_spinkit: "^1.0.0" # firebase - firebase_messaging: "^1.0.4" + firebase_core: ^0.5.3 + firebase_messaging: ^7.0.3 # icons # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: "^0.1.2" - community_material_icon: "^0.1.2" + community_material_icon: "^5.4.55" dev_dependencies: flutter_test: sdk: flutter - build_runner: ^0.8.0 - json_serializable: ^0.5.0 + build_runner: "^1.0.0" + json_serializable: ^3.5.1 + test: ^1.15.7 # For information on the generic Dart part of this file, see the # following page: https://www.dartlang.org/tools/pub/pubspec @@ -119,3 +125,7 @@ flutter: # # For details regarding fonts from package dependencies, # see https://flutter.io/custom-fonts/#from-packages + +# https://github.com/flutter/flutter/issues/70433 +dependency_overrides: + intl: ^0.17.0-nullsafety.2