Last changes not published

This commit is contained in:
vjrj 2026-03-05 01:18:27 +01:00
parent ac65ccf990
commit 21ec08303a
43 changed files with 607 additions and 613 deletions

View file

@ -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<ActiveFiresPage> {
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<ActiveFiresPage> {
}
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<ActiveFiresPage> {
},
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<ActiveFiresPage> {
view.onRefresh(completer);
return completer.future;
}),
// TODO: Evaluate: https://github.com/tiagojencmartins/unicornspeeddial
new FabDialer(_fabMiniMenuItemList(context, view.onAdd),
fires600, new Icon(Icons.add))
])

View file

@ -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<Null> stream) {
if (options is AttributionPluginOptions) {
var style = new TextStyle(
// fontWeight: FontWeight.bold,

View file

@ -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),
);
}
}

View file

@ -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<CustomStepper> {
super.initState();
_keys = new List<GlobalKey>.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<CustomStepper> {
}
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<CustomStepper> {
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<CustomStepper> {
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<CustomStepper> {
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<CustomStepper> {
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<CustomStepper> {
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<CustomStepper> {
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<CustomStepper> {
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<CustomStepper> {
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<CustomStepper> {
);
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: <Widget>[
new Column(
children: <Widget>[
// 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: <Widget>[
new Column(children: <Widget>[
// 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<CustomStepper> {
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<CustomStepper> {
final List<Widget> children = <Widget>[];
for (int i = 0; i < widget.steps.length; i += 1) {
children.add(
new Column(
key: _keys[i],
children: <Widget>[
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: <Widget>[
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<CustomStepper> {
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: <Widget>[
new Container(
@ -593,7 +598,7 @@ class _CustomStepperState extends State<CustomStepper> {
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<CustomStepper> {
Widget build(BuildContext context) {
assert(debugCheckHasMaterial(context));
assert(() {
if (context.ancestorWidgetOfExactType(CustomStepper) != null)
if (context.findAncestorWidgetOfExactType<CustomStepper>() != 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<CustomStepper> {
// 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;

View file

@ -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<Null> stream) {
if (options is DummyMapPluginOptions) {
return const SizedBox();
}

View file

@ -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<String> getFileNameOfLang({@required String dir,@required String fileName,@required String ext,@required String lang}) async {
Future<String> 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<String> getFileNameOfLang({@required String dir,@required String fileNam
// https://github.com/flutter/flutter/issues/15325
Future<bool> assetNotExists(String asset) {
print('Does not exists $asset ?');
return rootBundle.load(asset).then((_) => false).catchError((err, stack) { print (err); print(stack); return true;});
}
return rootBundle.load(asset).then((_) => false).catchError((err, stack) {
print(err);
print(stack);
return true;
});
}

View file

@ -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<FireAlert> {
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<FireAlert> {
);
}
Widget buildTweetButton() {
return new Align(
alignment: const Alignment(0.0, -0.2),
@ -62,9 +61,11 @@ class _FireAlertState extends State<FireAlert> {
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<FireAlert> {
buildCallButton()
])),
new CustomStep(
title: new Text(S.of(context).notifyNeighbours),
// state: CustomStepState.disabled,
content: new Column(children: <Widget>[
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: <Widget>[
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),

View file

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

View file

@ -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<FireNotificationList> {
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<FireNotificationList> {
}
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<FireNotificationList> {
return new StoreConnector<AppState, _ViewModel>(
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<FireNotificationList> {
},
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<FireNotificationList> {
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: <Widget>[
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: <Widget>[
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));
});
}

View file

@ -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<FiresApp> {
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<AppState> store;
// globals.getIt.registerSingleton
_FiresAppState(this.store);
@override
Widget build(BuildContext context) {
StatefulWidget home = new MaterialAppWithIntroHome(
@ -82,7 +79,7 @@ class _FiresAppState extends State<FiresApp> {
}
return S.of(context).appName;
},
theme: isDevelopment? devFiresTheme: firesTheme,
theme: isDevelopment ? devFiresTheme : firesTheme,
routes: routes));
}
}

View file

@ -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<genericMap> {
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<genericMap> {
}
},
// 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<genericMap> {
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<genericMap> {
bool isNotif,
OnFirePressedInMap onFirePressed) {
List<Marker> 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) {

View file

@ -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<Widget> buildActionList(YourLocation loc, BuildContext context,
int kmAround, GlobalKey<ScaffoldState> scaffoldState) {
List<Widget> actionList = new List<Widget>();
List<Widget> actionList = [];
switch (state.status) {
case FireMapStatus.edit:
actionList.add(new FlatButton(
@ -74,8 +74,7 @@ class GenericMapBottom extends StatelessWidget {
children: <Widget>[
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),
));

View file

@ -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<HomePage> {
void initState() {
super.initState();
_firebaseMessaging.configure(onMessage: (Map<String, dynamic> 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<String, dynamic> message) {
debugPrint("onLaunch (isLoaded: ${store.state.isLoaded}): $message");
_notifForMessage(message, store.state.isLoaded);
_navigateToItemDetail(message);
return;
}, onResume: (Map<String, dynamic> 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<HomePage> {
});
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<HomePage> {
),
),
]),
body: !view.isLoaded? new FiresSpinner(): new SafeArea(
child: Center(
child: new CenteredColumn(children: <Widget>[
new Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
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: <Widget>[
new Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
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: <Widget>[
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<HomePage> {
// https://pub.dartlang.org/packages/firebase_messaging#-example-tab-
final FirebaseMessaging _firebaseMessaging = new FirebaseMessaging();
FireNotification _notifForMessage(Map<String, dynamic> message, bool isLoaded) {
FireNotification _notifForMessage(
Map<String, dynamic> 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<HomePage> {
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;
}
}

View file

@ -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<AppState> 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<Null> stream) {
Store<AppState> store = Injector.getInjector().get<Store<AppState>>();
if (options is LayerSelectorMapPluginOptions) {
return LayoutBuilder(

View file

@ -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<YourLocation> getUserLocation(
GlobalKey<ScaffoldState> scaffoldKey) async {
// Platform messages may fail, so we use a try/catch PlatformException.
try {
Location _location = new Location();
Map<String, double> 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<YourLocation> 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<String> getReverseLocation({@required lat, @required lon,
bool external = false}) async {
Future<String> getReverseLocation(
{@required lat, @required lon, bool external = false}) async {
final coordinates = new Coordinates(lat, lon);
var geoCoder = external ? Geocoder.google(Injector.getInjector().get<String>(key: "gmapKey")) : Geocoder.local;
var geoCoder = external
? Geocoder.google(Injector.getInjector().get<String>(key: "gmapKey"))
: Geocoder.local;
var addresses = await geoCoder.findAddressesFromCoordinates(coordinates);
var first = addresses.first;
print("${first.featureName} : ${first.addressLine}");

View file

@ -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<PackageInfo> loadPackageInfo() async {
@ -22,13 +22,14 @@ Future<PackageInfo> loadPackageInfo() async {
Future<Map<String, dynamic>> 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<Middleware<AppState>> otherMiddleware) {
WidgetsFlutterBinding.ensureInitialized();
final injector = Injector.getInjector();
injector.map<FiresApi>((i) => new FiresApi(), isSingleton: true);
loadPackageInfo().then((packageInfo) {
@ -36,13 +37,12 @@ void mainCommon(List<Middleware<AppState>> otherMiddleware) {
print('Running version ${packageInfo.version}');
loadSecrets().then((secrets) {
final store = new Store<AppState>(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<Store<AppState>>((i) => store);
injector.map<String>((i) => store.state.firesApiUrl, key: "firesApiUrl");
@ -56,9 +56,9 @@ void mainCommon(List<Middleware<AppState>> otherMiddleware) {
}
// https://flutter.io/cookbook/maintenance/error-reporting/
runZoned<Future<Null>>(() async {
runZonedGuarded<Future<void>>(() 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);

View file

@ -30,7 +30,7 @@ LoggingMiddleware customLogPrinter<State>({
void main() {
globals.isDevelopment = true;
print("Is development!");
String onlyLogActionFormatter<State>(
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<Middleware> devMiddlewares = logRedux == LogLevel.none
List<Middleware> devMiddlewares = logRedux == LogLevel.actions
? []
: [
customLogPrinter(

View file

@ -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: <Widget>[
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);

View file

@ -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}';
}
}

View file

@ -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<String, dynamic> json) => new AppState(
yourLocations: (json['yourLocations'] as List)
.map((e) => new YourLocation.fromJson(e as Map<String, dynamic>))
.toList(),
fireNotifications: (json['fireNotifications'] as List)
.map((e) => new FireNotification.fromJson(e as Map<String, dynamic>))
.toList());
abstract class _$AppStateSerializerMixin {
List<YourLocation> get yourLocations;
List<FireNotification> get fireNotifications;
Map<String, dynamic> 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<String, dynamic>.from(e as Map)))
.toList()),
fireNotifications: $checkedConvert(
json,
'fireNotifications',
(v) => (v as List)
.map((e) => FireNotification.fromJson(
Map<String, dynamic>.from(e as Map)))
.toList()),
);
return val;
});
}
class _$AppStateJsonMapWrapper extends $JsonMapWrapper {
final _$AppStateSerializerMixin _v;
_$AppStateJsonMapWrapper(this._v);
@override
Iterable<String> 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<String, dynamic> _$AppStateToJson(AppState instance) => <String, dynamic>{
'yourLocations': instance.yourLocations.map((e) => e.toJson()).toList(),
'fireNotifications':
instance.fireNotifications.map((e) => e.toJson()).toList(),
};

View file

@ -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<String, Route<Null>> routes = <String, Route<Null>>{};
Map<String, dynamic> toJson() => _$FireNotificationToJson(this);
/*
Route<Null> getRoute(Store store) {

View file

@ -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<String, dynamic> 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<String, dynamic> 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<String> 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<String, dynamic> _$FireNotificationToJson(FireNotification instance) =>
<String, dynamic>{
'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,
};

View file

@ -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<List<FireNotification>> loadFireNotifications() async {
return await globals.prefs.then((prefs) {
List<String> FireNotifications = prefs.getStringList(fireNotificationKey);
List<FireNotification> persistedList = List<FireNotification>();
List<FireNotification> persistedList = [];
if (FireNotifications == null) {
FireNotifications = [];
// first run, init with empty list

View file

@ -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<String> createUser(AppState state, String mobileToken,
String lang) async {
Future<String> 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<List<YourLocation>> 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<YourLocation> 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<UpdateFireMapStatsAction> 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<List<Polyline>> 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<Polyline> union = [];
final multipolygon =
json.decode(resultDecoded['data']['union']['value'])['geometry']
['coordinates'];
json.decode(resultDecoded['data']['union']['value'])['geometry']
['coordinates'];
for (List<dynamic> polygon in multipolygon) {
for (List<dynamic> hole in polygon) {
List<LatLng> 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<bool> 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));

View file

@ -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<String, dynamic> 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

View file

@ -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<String, dynamic> 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<String, dynamic> 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<String> 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<String, dynamic> _$YourLocationToJson(YourLocation instance) =>
<String, dynamic>{
'id': objectIdToJson(instance.id),
'lat': instance.lat,
'lon': instance.lon,
'description': instance.description,
'subscribed': instance.subscribed,
'distance': instance.distance,
'currentNumFires': instance.currentNumFires,
};

View file

@ -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<List<YourLocation>> loadYourLocations() async {
return await globals.prefs.then((prefs) {
List<String> yourLocations = prefs.getStringList(locationKey);
List<YourLocation> persistedList = List<YourLocation>();
List<YourLocation> persistedList = [];
if (yourLocations == null) {
yourLocations = [];
// first run, init with empty list

View file

@ -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();
}
}

View file

@ -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<YourLocation> openPlacesDialog(GlobalKey<ScaffoldState> sc) async {
Mode _mode = Mode.overlay;
String gmapKey = Injector.getInjector().get<String>(key: "gmapKey");
@ -16,7 +16,7 @@ Future<YourLocation> openPlacesDialog(GlobalKey<ScaffoldState> 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');
},

View file

@ -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<AppState> store, action, NextDispatcher next) {
.then((List<YourLocation> 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<AppState> store, FireNotification notif) {
}
void unsubsViaApi(Store<AppState> 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<AppState> store, ObjectId id, onUnsubs) {
void subscribeViaApi(Store<AppState> 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);
});

View file

@ -28,7 +28,8 @@ List<FireNotification> _deletedFireNotification(
}
List<FireNotification> _updatedFireNotification(
List<FireNotification> notifications, UpdatedFireNotificationAction action) {
List<FireNotification> notifications,
UpdatedFireNotificationAction action) {
return notifications
.map((notif) => notif.id == action.notif.id ? action.notif : notif)
.toList();
@ -45,5 +46,5 @@ List<FireNotification> _readedFireNotification(
List<FireNotification> _deletedAllFireNotifications(
List<FireNotification> notifications,
DeletedAllFireNotificationAction action) {
return new List<FireNotification>();
return [];
}

View file

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

View file

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

View file

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

View file

@ -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<Null> stream) {
if (options is ZoomMapPluginOptions) {
/* print('point ${mapState
.getPixelBounds(mapState.zoom)