More work with fire notifications

This commit is contained in:
Vicente J. Ruiz Jurado 2018-07-31 12:51:10 +02:00
parent 5fdaf239c0
commit 9dd8a7de97
24 changed files with 512 additions and 288 deletions

View file

@ -5,17 +5,18 @@ import 'package:fires_flutter/models/yourLocation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_fab_dialer/flutter_fab_dialer.dart'; import 'package:flutter_fab_dialer/flutter_fab_dialer.dart';
import 'package:flutter_redux/flutter_redux.dart'; import 'package:flutter_redux/flutter_redux.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart';
import 'package:redux/src/store.dart'; import 'package:redux/src/store.dart';
import 'colors.dart'; import 'colors.dart';
import 'generated/i18n.dart'; import 'generated/i18n.dart';
import 'genericMap.dart';
import 'globalFiresBottomStats.dart'; import 'globalFiresBottomStats.dart';
import 'locationUtils.dart'; import 'locationUtils.dart';
import 'mainDrawer.dart'; import 'mainDrawer.dart';
import 'models/appState.dart'; import 'models/appState.dart';
import 'placesAutocompleteUtils.dart'; import 'placesAutocompleteUtils.dart';
import 'redux/actions.dart'; import 'redux/actions.dart';
import 'genericMap.dart';
@immutable @immutable
class _ViewModel { class _ViewModel {
@ -24,35 +25,36 @@ class _ViewModel {
final DeleteYourLocationFunction onDelete; final DeleteYourLocationFunction onDelete;
final ToggleSubscriptionFunction onToggleSubs; final ToggleSubscriptionFunction onToggleSubs;
final OnLocationTapFunction onTap; final OnLocationTapFunction onTap;
final bool isLoading;
_ViewModel( _ViewModel(
{@required this.onAdd, {@required this.onAdd,
@required this.onDelete, @required this.onDelete,
@required this.onToggleSubs, @required this.onToggleSubs,
@required this.onTap, @required this.onTap,
@required this.yourLocations}); @required this.yourLocations,
@required this.isLoading});
@override @override
bool operator ==(Object other) { bool operator ==(Object other) =>
return identical(this, other) || identical(this, other) ||
other is _ViewModel && other is _ViewModel &&
runtimeType == other.runtimeType && runtimeType == other.runtimeType &&
yourLocations == other.yourLocations; yourLocations == other.yourLocations &&
} isLoading == other.isLoading;
@override @override
int get hashCode => yourLocations.hashCode; int get hashCode => yourLocations.hashCode ^ isLoading.hashCode;
} }
class ActiveFiresPage extends StatefulWidget { class ActiveFiresPage extends StatefulWidget {
static const String routeName = '/fires'; static const String routeName = '/fires';
@override @override
_ActiveFiresPageState createState() => _ActiveFiresPageState(); _ActiveFiresPageState createState() => _ActiveFiresPageState();
} }
class _ActiveFiresPageState extends State<ActiveFiresPage> { class _ActiveFiresPageState extends State<ActiveFiresPage> {
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>(); final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
List<FabMiniMenuItem> _fabMiniMenuItemList( List<FabMiniMenuItem> _fabMiniMenuItemList(
@ -171,7 +173,8 @@ class _ActiveFiresPageState extends State<ActiveFiresPage> {
onTap: (loc) { onTap: (loc) {
gotoLocationMap(store, loc, context); gotoLocationMap(store, loc, context);
}, },
yourLocations: store.state.yourLocations); yourLocations: store.state.yourLocations,
isLoading: !store.state.isLoaded);
}, },
builder: (context, view) { builder: (context, view) {
var hasLocations = view.yourLocations.length > 0; var hasLocations = view.yourLocations.length > 0;
@ -196,7 +199,9 @@ class _ActiveFiresPageState extends State<ActiveFiresPage> {
floatingActionButtonLocation: floatingActionButtonLocation:
FloatingActionButtonLocation.centerFloat, FloatingActionButtonLocation.centerFloat,
bottomNavigationBar: new GlobalFiresBottomStats(), bottomNavigationBar: new GlobalFiresBottomStats(),
body: hasLocations body: view.isLoading
? new SpinKitPulse(color: fires600)
: hasLocations
? new Stack(children: <Widget>[ ? new Stack(children: <Widget>[
_buildSavedLocations(context, view.yourLocations, _buildSavedLocations(context, view.yourLocations,
view.onDelete, view.onToggleSubs, view.onTap), view.onDelete, view.onToggleSubs, view.onTap),
@ -224,8 +229,8 @@ class _ActiveFiresPageState extends State<ActiveFiresPage> {
void gotoLocationMap( void gotoLocationMap(
Store<AppState> store, YourLocation loc, BuildContext context) { Store<AppState> store, YourLocation loc, BuildContext context) {
store.dispatch(new ShowYourLocationMapAction(loc)); store.dispatch(new ShowYourLocationMapAction(loc));
Navigator.push(context, Navigator.push(
new MaterialPageRoute(builder: (context) => new genericMap())); context, new MaterialPageRoute(builder: (context) => new genericMap()));
} }
void onAddYourLocation(AddYourLocationFunction onAdd) { void onAddYourLocation(AddYourLocationFunction onAdd) {

View file

@ -2,43 +2,49 @@ import 'dart:async';
import 'package:comunes_flutter/comunes_flutter.dart'; import 'package:comunes_flutter/comunes_flutter.dart';
import 'package:fires_flutter/models/fireNotification.dart'; import 'package:fires_flutter/models/fireNotification.dart';
import 'package:fires_flutter/models/yourLocation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_redux/flutter_redux.dart'; import 'package:flutter_redux/flutter_redux.dart';
import 'package:redux/src/store.dart'; import 'package:redux/src/store.dart';
import 'customMoment.dart'; import 'customMoment.dart';
import 'generated/i18n.dart'; import 'generated/i18n.dart';
import 'genericMap.dart';
import 'mainDrawer.dart'; import 'mainDrawer.dart';
import 'models/appState.dart'; import 'models/appState.dart';
import 'redux/actions.dart'; import 'redux/actions.dart';
import 'genericMap.dart';
@immutable @immutable
class _ViewModel { class _ViewModel {
final List<FireNotification> fireNotifications; final List<FireNotification> fireNotifications;
final int fireNotificationsUnread;
final List<YourLocation> yourLocations;
final TapFireNotificationFunction onTap; final TapFireNotificationFunction onTap;
//final OnReceivedFireNotificationFunction onReceived;
final DeleteFireNotificationFunction onDelete; final DeleteFireNotificationFunction onDelete;
final DeleteAllFireNotificationFunction onDeleteAll; final DeleteAllFireNotificationFunction onDeleteAll;
_ViewModel( _ViewModel(
{@required this.onTap, {@required this.onTap,
// @required this.onReceived,
@required this.onDelete, @required this.onDelete,
@required this.onDeleteAll, @required this.onDeleteAll,
@required this.fireNotifications}); @required this.fireNotifications,
@required this.yourLocations,
@required this.fireNotificationsUnread});
@override @override
bool operator ==(Object other) { bool operator ==(Object other) =>
return identical(this, other) || identical(this, other) ||
other is _ViewModel && other is _ViewModel &&
runtimeType == other.runtimeType && runtimeType == other.runtimeType &&
fireNotifications == other.fireNotifications; fireNotifications == other.fireNotifications &&
} fireNotificationsUnread == other.fireNotificationsUnread &&
yourLocations == other.yourLocations;
@override @override
int get hashCode => fireNotifications.hashCode; int get hashCode =>
fireNotifications.hashCode ^
fireNotificationsUnread.hashCode ^
yourLocations.hashCode;
} }
class FireNotificationList extends StatefulWidget { class FireNotificationList extends StatefulWidget {
@ -51,12 +57,13 @@ class FireNotificationList extends StatefulWidget {
class _FireNotificationListState extends State<FireNotificationList> { class _FireNotificationListState extends State<FireNotificationList> {
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>(); final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
Widget _buildRow( Widget _buildRow(BuildContext context, List<YourLocation> yourLocations,
BuildContext context, FireNotification notif, onDeleted, onTap) { FireNotification notif, onDeleted, onTap) {
YourLocation yl = yourLocations.singleWhere((yl) => yl.id == notif.subsId);
return new ListTile( return new ListTile(
dense: true, dense: true,
leading: const Icon(Icons.whatshot), leading: const Icon(Icons.whatshot),
title: new Text(notif.description, title: new Text('${yl.description}. ${notif.description}',
style: new TextStyle( style: new TextStyle(
fontWeight: notif.read ? FontWeight.normal : FontWeight.bold)), fontWeight: notif.read ? FontWeight.normal : FontWeight.bold)),
subtitle: new Text(Moment.now().from(context, notif.when)), subtitle: new Text(Moment.now().from(context, notif.when)),
@ -74,13 +81,17 @@ class _FireNotificationListState extends State<FireNotificationList> {
)); ));
} }
Widget _buildSavedFireNotifications(BuildContext context, Widget _buildSavedFireNotifications(
List<FireNotification> notifList, onDeleted, onTap) { BuildContext context,
List<YourLocation> yourLocations,
List<FireNotification> notifList,
onDeleted,
onTap) {
return new RefreshIndicator( return new RefreshIndicator(
child: new ListView.builder( child: new ListView.builder(
padding: const EdgeInsets.all(16.0), padding: const EdgeInsets.all(16.0),
reverse: true, // reverse: true,
shrinkWrap: true, // shrinkWrap: true,
itemCount: notifList.length, itemCount: notifList.length,
itemBuilder: (BuildContext _context, int i) { itemBuilder: (BuildContext _context, int i) {
final ThemeData theme = Theme.of(context); final ThemeData theme = Theme.of(context);
@ -106,8 +117,8 @@ class _FireNotificationListState extends State<FireNotificationList> {
border: new Border( border: new Border(
bottom: bottom:
new BorderSide(color: theme.dividerColor))), new BorderSide(color: theme.dividerColor))),
child: _buildRow( child: _buildRow(context, yourLocations,
context, notifList.elementAt(i), onDeleted, onTap))); notifList.elementAt(i), onDeleted, onTap)));
}), }),
onRefresh: _handleRefresh); onRefresh: _handleRefresh);
} }
@ -145,11 +156,13 @@ class _FireNotificationListState extends State<FireNotificationList> {
store.dispatch(new ReadFireNotificationAction( store.dispatch(new ReadFireNotificationAction(
notif.copyWith(read: true))); notif.copyWith(read: true)));
} }
new Timer(new Duration(milliseconds: 1000), () { new Timer(new Duration(milliseconds: 500), () {
gotoMap(store, notif, context); gotoMap(store, notif, context);
}); });
}, },
fireNotifications: store.state.fireNotifications); yourLocations: store.state.yourLocations,
fireNotifications: store.state.fireNotifications,
fireNotificationsUnread: store.state.fireNotificationsUnread);
}, },
builder: (context, view) { builder: (context, view) {
var hasFireNotifications = view.fireNotifications.length > 0; var hasFireNotifications = view.fireNotifications.length > 0;
@ -191,7 +204,7 @@ class _FireNotificationListState extends State<FireNotificationList> {
style: new TextStyle( style: new TextStyle(
height: 1.3, color: Colors.black45)) height: 1.3, color: Colors.black45))
])))) ]))))
: _buildSavedFireNotifications(context, : _buildSavedFireNotifications(context, view.yourLocations,
view.fireNotifications, view.onDelete, view.onTap)); view.fireNotifications, view.onDelete, view.onTap));
}); });
} }
@ -199,8 +212,8 @@ class _FireNotificationListState extends State<FireNotificationList> {
void gotoMap( void gotoMap(
Store<AppState> store, FireNotification notif, BuildContext context) { Store<AppState> store, FireNotification notif, BuildContext context) {
store.dispatch(new ShowFireNotificationMapAction(notif)); store.dispatch(new ShowFireNotificationMapAction(notif));
Navigator.push(context, Navigator.push(
new MaterialPageRoute(builder: (context) => new genericMap())); context, new MaterialPageRoute(builder: (context) => new genericMap()));
} }
_showConfirmDialog(_ViewModel view) { _showConfirmDialog(_ViewModel view) {

View file

@ -1,46 +0,0 @@
import 'package:bson_objectid/bson_objectid.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:fires_flutter/models/fireNotification.dart';
import 'package:redux/src/store.dart';
import 'models/appState.dart';
import 'redux/actions.dart';
final FirebaseMessaging _firebaseMessaging = new FirebaseMessaging();
// https://pub.dartlang.org/packages/firebase_messaging#-example-tab-
void firebaseConfig(Store<AppState> store) {
_firebaseMessaging.configure(onMessage: (Map<String, dynamic> message) {
print("onMessage: $message");
onMessage(message, store);
//_showItemDialog(message);
}, onLaunch: (Map<String, dynamic> message) {
print("onLaunch: $message");
//_navigateToItemDetail(message);
onMessage(message, store);
}, onResume: (Map<String, dynamic> message) {
print("onResume: $message");
//_navigateToItemDetail(message);
onMessage(message, store);
});
getToken(store);
}
void onMessage(Map<String, dynamic> message, Store<AppState> store) {
FireNotification notif = new FireNotification(
id: new ObjectId.fromHexString(message['id']),
lat: double.parse(message['lat']),
lon: double.parse(message['lon']),
description: message['description'],
read: false,
when: DateTime.parse(message['when']));
print(notif);
store.dispatch(new AddFireNotificationAction(notif));
}
getToken(Store<AppState> store) async {
String token = await _firebaseMessaging.onTokenRefresh.first;
// print(token);
store.dispatch(new OnUserTokenAction(token));
}

View file

@ -1,23 +1,38 @@
import 'package:bson_objectid/bson_objectid.dart';
import 'package:comunes_flutter/comunes_flutter.dart'; import 'package:comunes_flutter/comunes_flutter.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:fires_flutter/models/fireNotification.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_redux/flutter_redux.dart'; import 'package:flutter_redux/flutter_redux.dart';
import 'package:redux/redux.dart'; import 'package:redux/redux.dart';
import 'package:redux/src/store.dart';
import 'activeFires.dart'; import 'activeFires.dart';
import 'fireAlert.dart';
import 'fireNotificationList.dart';
import 'generated/i18n.dart'; import 'generated/i18n.dart';
import 'homePage.dart'; import 'homePage.dart';
import 'introPage.dart'; import 'introPage.dart';
import 'models/appState.dart'; import 'models/appState.dart';
import 'privacyPage.dart';
import 'redux/actions.dart'; import 'redux/actions.dart';
import 'sandbox.dart'; import 'sandbox.dart';
import 'theme.dart';
import 'privacyPage.dart';
import 'fireAlert.dart';
import 'supportPage.dart'; import 'supportPage.dart';
import 'fireNotificationList.dart'; import 'theme.dart';
class FiresApp extends StatelessWidget { class FiresApp extends StatefulWidget {
FiresApp(this.store);
final Store<AppState> store;
@override
_FiresAppState createState() => _FiresAppState(store);
}
class _FiresAppState extends State<FiresApp> {
final GlobalKey<NavigatorState> navigatorKey =
new GlobalKey<NavigatorState>();
static final WidgetBuilder introWidget = (context) => new IntroPage(); static final WidgetBuilder introWidget = (context) => new IntroPage();
static final WidgetBuilder continueWidget = (context) => new HomePage(); static final WidgetBuilder continueWidget = (context) => new HomePage();
@ -29,22 +44,23 @@ class FiresApp extends StatelessWidget {
Sandbox.routeName: (BuildContext context) => new Sandbox(), Sandbox.routeName: (BuildContext context) => new Sandbox(),
FireAlert.routeName: (BuildContext context) => new FireAlert(), FireAlert.routeName: (BuildContext context) => new FireAlert(),
SupportPage.routeName: (BuildContext context) => new SupportPage(), SupportPage.routeName: (BuildContext context) => new SupportPage(),
FireNotificationList.routeName: (BuildContext context) => new FireNotificationList(), FireNotificationList.routeName: (BuildContext context) =>
new FireNotificationList(),
}; };
final Store<AppState> store; final Store<AppState> store;
// globals.getIt.registerSingleton // globals.getIt.registerSingleton
FiresApp(this.store); _FiresAppState(this.store);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
StatefulWidget home = new MaterialAppWithIntroHome( StatefulWidget home = new MaterialAppWithIntroHome(
introWidget, continueWidget, 'showInitialWizard-2018-06-27-01'); introWidget, continueWidget, 'showInitialWizard-2018-06-27-01');
return new StoreProvider<AppState>( return new StoreProvider<AppState>(
store: this.store, store: this.store,
child: new MaterialApp( child: new MaterialApp(
navigatorKey: navigatorKey,
localizationsDelegates: [ localizationsDelegates: [
S.delegate, S.delegate,
GlobalMaterialLocalizations.delegate, GlobalMaterialLocalizations.delegate,
@ -65,4 +81,97 @@ class FiresApp extends StatelessWidget {
theme: firesTheme, theme: firesTheme,
routes: routes)); routes: routes));
} }
@override
void initState() {
super.initState();
_firebaseMessaging.configure(onMessage: (Map<String, dynamic> message) {
print("onMessage in fireApp: $message");
_showItemDialog(message, store);
}, onLaunch: (Map<String, dynamic> message) {
print("onLaunch: $message");
_navigateToItemDetail(message, store);
}, onResume: (Map<String, dynamic> message) {
print("onResume: $message");
_navigateToItemDetail(message, store);
});
_firebaseMessaging.requestNotificationPermissions(
const IosNotificationSettings(sound: true, badge: true, alert: true));
_firebaseMessaging.onIosSettingsRegistered
.listen((IosNotificationSettings settings) {
print("Settings registered: $settings");
});
_firebaseMessaging.getToken().then((String token) {
assert(token != null);
store.dispatch(new OnUserTokenAction(token));
setState(() {});
});
}
void _showItemDialog(Map<String, dynamic> message, Store store) {
final notif = _notifForMessage(message);
showDialog<bool>(
context: navigatorKey.currentContext,
builder: (_) => _buildDialog(navigatorKey.currentContext, notif),
).then((bool shouldNavigate) {
if (shouldNavigate == true) {
_navigateToItemDetail(message, store);
}
}).catchError((e) => print("$e"));
}
Widget _buildDialog(BuildContext context, FireNotification item) {
return new AlertDialog(
content: new Text(item.description),
actions: <Widget>[
new FlatButton(
child: Text(S.of(context).CLOSE),
onPressed: () {
Navigator.pop(context, false);
},
),
new FlatButton(
child: Text(S.of(context).SHOW),
onPressed: () {
Navigator.pop(context, true);
},
),
],
);
}
void _navigateToItemDetail(Map<String, dynamic> message, Store store) {
FireNotification notif = _notifForMessage(message);
// Clear away dialogs
Navigator.popUntil(navigatorKey.currentContext,
(Route<dynamic> route) => route is PageRoute);
if (!notif.getRoute(store).isCurrent) {
Navigator.push(navigatorKey.currentContext, notif.getRoute(store));
}
}
// https://pub.dartlang.org/packages/firebase_messaging#-example-tab-
final FirebaseMessaging _firebaseMessaging = new FirebaseMessaging();
FireNotification _notifForMessage(Map<String, dynamic> message) {
FireNotification notif;
print('start parse notif');
try {
notif = new FireNotification(
id: new ObjectId.fromHexString(message['id']),
lat: double.parse(message['lat']),
lon: double.parse(message['lon']),
description: message['description'],
read: false,
when: DateTime.parse(message['when']),
sealed: message['sealed'],
subsId: new ObjectId.fromHexString(message['subsId']));
print('end parse notif');
debugPrint(notif.toString());
} catch (e) {
debugPrint(e.toString());
}
store.dispatch(new AddFireNotificationAction(notif));
return notif;
}
} }

View file

@ -23,8 +23,10 @@ class S implements WidgetsLocalizations {
String get AvoidThisStringsIfisNotPlural => "Zero One Two Few Many Other"; String get AvoidThisStringsIfisNotPlural => "Zero One Two Few Many Other";
String get CANCEL => "CANCEL"; String get CANCEL => "CANCEL";
String get CLOSE => "CLOSE";
String get DELETE => "DELETE"; String get DELETE => "DELETE";
String get SAVE => "SAVE"; String get SAVE => "SAVE";
String get SHOW => "SHOW";
String get UNDO => "UNDO"; String get UNDO => "UNDO";
String get aDay => "a day"; String get aDay => "a day";
String get aF3wSeconds => "a few seconds"; String get aF3wSeconds => "a few seconds";
@ -111,6 +113,8 @@ class es extends S {
@override @override
String get callEmergencyServicesDescription => "Deberías llamar al 112 si no lo has hecho ya para avisar a los servicios de emergencia."; String get callEmergencyServicesDescription => "Deberías llamar al 112 si no lo has hecho ya para avisar a los servicios de emergencia.";
@override @override
String get CLOSE => "CERRAR";
@override
String get errorFirePlaceDialog => "No hemos podido obtener la ubicación del fuego"; String get errorFirePlaceDialog => "No hemos podido obtener la ubicación del fuego";
@override @override
String get toDeleteThisPlace => "Desliza horizontalmente para borrar este lugar"; String get toDeleteThisPlace => "Desliza horizontalmente para borrar este lugar";
@ -163,6 +167,8 @@ class es extends S {
@override @override
String get SAVE => "GUARDAR"; String get SAVE => "GUARDAR";
@override @override
String get SHOW => "MOSTRAR";
@override
String get addedThisLocation => "Ya has añadido este lugar antes"; String get addedThisLocation => "Ya has añadido este lugar antes";
@override @override
String get typeTheNameOfAPlace => "Escribe el nombre de un lugar, zona, etc"; String get typeTheNameOfAPlace => "Escribe el nombre de un lugar, zona, etc";

View file

@ -1,7 +1,6 @@
import 'dart:core'; import 'dart:core';
import 'package:comunes_flutter/comunes_flutter.dart'; import 'package:comunes_flutter/comunes_flutter.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:fires_flutter/models/basicLocation.dart'; import 'package:fires_flutter/models/basicLocation.dart';
import 'package:fires_flutter/models/yourLocation.dart'; import 'package:fires_flutter/models/yourLocation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -9,6 +8,7 @@ import 'package:flutter_map/flutter_map.dart';
import 'package:flutter_map/plugin_api.dart'; import 'package:flutter_map/plugin_api.dart';
import 'package:flutter_redux/flutter_redux.dart'; import 'package:flutter_redux/flutter_redux.dart';
import 'package:latlong/latlong.dart'; import 'package:latlong/latlong.dart';
import 'package:share/share.dart';
import 'colors.dart'; import 'colors.dart';
import 'dummyMapPlugin.dart'; import 'dummyMapPlugin.dart';
@ -25,6 +25,7 @@ import 'zoomMapPlugin.dart';
@immutable @immutable
class _ViewModel { class _ViewModel {
final String serverUrl;
final FireMapState mapState; final FireMapState mapState;
final OnSubscribeFunction onSubs; final OnSubscribeFunction onSubs;
final OnSubscribeConfirmedFunction onSubsConfirmed; final OnSubscribeConfirmedFunction onSubsConfirmed;
@ -37,6 +38,7 @@ class _ViewModel {
_ViewModel( _ViewModel(
{@required this.mapState, {@required this.mapState,
@required this.serverUrl,
@required this.onSubs, @required this.onSubs,
@required this.onSubsConfirmed, @required this.onSubsConfirmed,
@required this.onUnSubs, @required this.onUnSubs,
@ -66,7 +68,7 @@ class _genericMapState extends State<genericMap> {
// This needs to be stateful so when resizes don't get a new globalkey // This needs to be stateful so when resizes don't get a new globalkey
// https://github.com/flutter/flutter/issues/1632#issuecomment-180478202 // https://github.com/flutter/flutter/issues/1632#issuecomment-180478202
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>(); final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
final FirebaseMessaging _firebaseMessaging = new FirebaseMessaging();
YourLocation _location; YourLocation _location;
YourLocation _initialLocation; YourLocation _initialLocation;
@ -105,6 +107,7 @@ class _genericMapState extends State<genericMap> {
store.dispatch(new UpdateYourLocationMapAction(loc)); store.dispatch(new UpdateYourLocationMapAction(loc));
store.dispatch(new EditConfirmYourLocationAction(loc)); store.dispatch(new EditConfirmYourLocationAction(loc));
}, },
serverUrl: store.state.firesApiUrl,
mapState: store.state.fireMapState); mapState: store.state.fireMapState);
}, },
builder: (context, view) { builder: (context, view) {
@ -223,8 +226,6 @@ class _genericMapState extends State<genericMap> {
break; break;
case FireMapStatus.subscriptionConfirm: case FireMapStatus.subscriptionConfirm:
view.onSubsConfirmed(_location); view.onSubsConfirmed(_location);
// IOS specific
_firebaseMessaging.requestNotificationPermissions();
break; break;
case FireMapStatus.unsubscribe: case FireMapStatus.unsubscribe:
view.onUnSubs(_location); view.onUnSubs(_location);
@ -299,6 +300,14 @@ class _genericMapState extends State<genericMap> {
icon: new Icon(Icons.save), icon: new Icon(Icons.save),
onPressed: () => view.onEditConfirm(_location)) onPressed: () => view.onEditConfirm(_location))
]; ];
case FireMapStatus.viewFireNotification:
return <Widget>[
new IconButton(
icon: new Icon(Icons.share),
onPressed: () {
Share.share('${view.mapState.fireNotification.description}. ${view.serverUrl}fire/${view.mapState.fireNotification.sealed}');
})
];
default: default:
return <Widget>[]; return <Widget>[];
} }

View file

@ -7,7 +7,6 @@ import 'package:flutter_simple_dependency_injection/injector.dart';
import 'package:redux/redux.dart'; import 'package:redux/redux.dart';
import 'package:sentry/sentry.dart'; import 'package:sentry/sentry.dart';
import 'firebaseMessagingConf.dart';
import 'firesApp.dart'; import 'firesApp.dart';
import 'globals.dart' as globals; import 'globals.dart' as globals;
import 'models/appState.dart'; import 'models/appState.dart';
@ -27,6 +26,7 @@ void mainCommon(List<Middleware<AppState>> otherMiddleware) {
initialState: new AppState( initialState: new AppState(
gmapKey: secrets['gmapKey'], gmapKey: secrets['gmapKey'],
firesApiKey: secrets['firesApiKey'], firesApiKey: secrets['firesApiKey'],
serverUrl: secrets['firesApiUrl'],
firesApiUrl: secrets['firesApiUrl'] + "api/v1/"), firesApiUrl: secrets['firesApiUrl'] + "api/v1/"),
middleware: List.from(otherMiddleware) middleware: List.from(otherMiddleware)
..add(fetchDataMiddleware)); ..add(fetchDataMiddleware));
@ -37,8 +37,6 @@ void mainCommon(List<Middleware<AppState>> otherMiddleware) {
VoidCallback mainFn = () { VoidCallback mainFn = () {
loadYourLocations().then((yl) { loadYourLocations().then((yl) {
firebaseConfig(store);
// Run baby run! // Run baby run!
runApp(new FiresApp(store)); runApp(new FiresApp(store));
}); });

View file

@ -17,7 +17,7 @@ void main() {
return ">>>>> ${action.toString().replaceAll('Instance of ', '')}"; return ">>>>> ${action.toString().replaceAll('Instance of ', '')}";
} }
LogLevel logRedux = LogLevel.actions; LogLevel logRedux = LogLevel.full;
List<Middleware> devMiddlewares = logRedux == LogLevel.none List<Middleware> devMiddlewares = logRedux == LogLevel.none
? [] ? []

View file

@ -1,16 +1,38 @@
import 'package:flutter/material.dart'; import 'package:badge/badge.dart';
import 'colors.dart';
import 'sandbox.dart';
import 'activeFires.dart';
import 'globals.dart' as globals;
import 'generated/i18n.dart';
import 'package:comunes_flutter/comunes_flutter.dart'; import 'package:comunes_flutter/comunes_flutter.dart';
import 'privacyPage.dart'; import 'package:flutter/material.dart';
import 'package:flutter_redux/flutter_redux.dart';
import 'activeFires.dart';
import 'colors.dart';
import 'fireAlert.dart'; import 'fireAlert.dart';
import 'supportPage.dart';
import 'fireNotificationList.dart'; import 'fireNotificationList.dart';
import 'package:community_material_icon/community_material_icon.dart'; import 'generated/i18n.dart';
import 'globals.dart' as globals;
import 'models/appState.dart';
import 'privacyPage.dart';
import 'sandbox.dart';
import 'supportPage.dart';
@immutable
class _ViewModel {
final int unreadCount;
_ViewModel({
@required this.unreadCount,
});
@override
bool operator ==(Object other) {
return identical(this, other) ||
other is _ViewModel &&
runtimeType == other.runtimeType &&
unreadCount == other.unreadCount;
}
@override
int get hashCode => unreadCount.hashCode;
}
class MainDrawer extends Drawer { class MainDrawer extends Drawer {
MainDrawer(BuildContext context, {key}) MainDrawer(BuildContext context, {key})
@ -18,6 +40,12 @@ class MainDrawer extends Drawer {
} }
Widget mainDrawer(BuildContext context) { Widget mainDrawer(BuildContext context) {
return new StoreConnector<AppState, _ViewModel>(
distinct: true,
converter: (store) {
return new _ViewModel(unreadCount: store.state.fireNotificationsUnread);
},
builder: (context, view) {
return new ListView( return new ListView(
// Important: Remove any padding from the ListView. // Important: Remove any padding from the ListView.
padding: EdgeInsets.zero, padding: EdgeInsets.zero,
@ -67,7 +95,13 @@ Widget mainDrawer(BuildContext context) {
), ),
new ListTile( new ListTile(
leading: const Icon(Icons.notifications), leading: const Icon(Icons.notifications),
title: new Text(S.of(context).fireNotificationsTitleShort), 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),
onTap: () { onTap: () {
// Then close the drawer // Then close the drawer
Navigator.pop(context); Navigator.pop(context);
@ -93,26 +127,28 @@ Widget mainDrawer(BuildContext context) {
Navigator.pushNamed(context, PrivacyPage.routeName); Navigator.pushNamed(context, PrivacyPage.routeName);
}, },
), ),
globals.isDevelopment ? globals.isDevelopment
new ListTile( ? new ListTile(
leading: const Icon(Icons.bug_report), leading: const Icon(Icons.bug_report),
title: new Text('Sandbox'), title: new Text('Sandbox'),
onTap: () { onTap: () {
Navigator.pop(context); Navigator.pop(context);
Navigator.pushNamed(context, Sandbox.routeName); Navigator.pushNamed(context, Sandbox.routeName);
}, },
): null, )
: null,
new AboutListTile( new AboutListTile(
icon: globals.appIcon, icon: globals.appIcon,
applicationName: S.of(context).appName, applicationName: S.of(context).appName,
applicationVersion: globals.appVersion, applicationVersion: globals.appVersion,
applicationIcon: globals.appMediumIcon, applicationIcon: globals.appMediumIcon,
applicationLegalese: S.of(context).appLicense(DateTime.now().year.toString()), applicationLegalese:
aboutBoxChildren: <Widget> [ S.of(context).appLicense(DateTime.now().year.toString()),
aboutBoxChildren: <Widget>[
// new Text('What?') // new Text('What?')
] ]
// FIXME // FIXME
) )
]) ]));
); });
} }

View file

@ -1,6 +1,6 @@
import 'package:comunes_flutter/comunes_flutter.dart'; 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/fireNotification.dart';
import 'package:fires_flutter/models/yourLocation.dart';
import 'package:json_annotation/json_annotation.dart'; import 'package:json_annotation/json_annotation.dart';
import 'package:meta/meta.dart'; import 'package:meta/meta.dart';
@ -25,12 +25,16 @@ class AppState extends Object with _$AppStateSerializerMixin {
@JsonKey(ignore: true) @JsonKey(ignore: true)
final String gmapKey; final String gmapKey;
@JsonKey(ignore: true) @JsonKey(ignore: true)
final String serverUrl;
@JsonKey(ignore: true)
final String firesApiKey; final String firesApiKey;
@JsonKey(ignore: true) @JsonKey(ignore: true)
final String firesApiUrl; final String firesApiUrl;
final List<YourLocation> yourLocations; final List<YourLocation> yourLocations;
final List<FireNotification> fireNotifications; final List<FireNotification> fireNotifications;
@JsonKey(ignore: true) @JsonKey(ignore: true)
final int fireNotificationsUnread;
@JsonKey(ignore: true)
final FireMapState fireMapState; final FireMapState fireMapState;
@JsonKey(ignore: true) @JsonKey(ignore: true)
@ -40,6 +44,7 @@ class AppState extends Object with _$AppStateSerializerMixin {
AppState( AppState(
{this.yourLocations: const <YourLocation>[], {this.yourLocations: const <YourLocation>[],
this.fireNotifications: const <FireNotification>[], this.fireNotifications: const <FireNotification>[],
this.fireNotificationsUnread: 0,
this.user: const User.initial(), this.user: const User.initial(),
this.isLoading: false, this.isLoading: false,
this.isLoaded: false, this.isLoaded: false,
@ -47,6 +52,7 @@ class AppState extends Object with _$AppStateSerializerMixin {
this.gmapKey, this.gmapKey,
this.firesApiKey, this.firesApiKey,
this.firesApiUrl, this.firesApiUrl,
this.serverUrl,
this.fireMapState: const FireMapState.initial()}); this.fireMapState: const FireMapState.initial()});
AppState copyWith( AppState copyWith(
@ -56,9 +62,11 @@ class AppState extends Object with _$AppStateSerializerMixin {
String error, String error,
String gmapKey, String gmapKey,
String firesApiKey, String firesApiKey,
String serverUrl,
String firesApiUrl, String firesApiUrl,
List<YourLocation> yourLocations, List<YourLocation> yourLocations,
List<FireNotification> fireNotifications, List<FireNotification> fireNotifications,
int fireNotificationsUnread,
FireMapState fireMapState}) { FireMapState fireMapState}) {
return new AppState( return new AppState(
isLoading: isLoading ?? this.isLoading, isLoading: isLoading ?? this.isLoading,
@ -68,8 +76,11 @@ class AppState extends Object with _$AppStateSerializerMixin {
gmapKey: gmapKey ?? this.gmapKey, gmapKey: gmapKey ?? this.gmapKey,
firesApiKey: firesApiKey ?? this.firesApiKey, firesApiKey: firesApiKey ?? this.firesApiKey,
firesApiUrl: firesApiUrl ?? this.firesApiUrl, firesApiUrl: firesApiUrl ?? this.firesApiUrl,
serverUrl: serverUrl ?? this.serverUrl,
yourLocations: yourLocations ?? this.yourLocations, yourLocations: yourLocations ?? this.yourLocations,
fireNotifications: fireNotifications ?? this.fireNotifications, fireNotifications: fireNotifications ?? this.fireNotifications,
fireNotificationsUnread:
fireNotificationsUnread ?? this.fireNotificationsUnread,
fireMapState: fireMapState ?? this.fireMapState); fireMapState: fireMapState ?? this.fireMapState);
} }
@ -77,8 +88,8 @@ class AppState extends Object with _$AppStateSerializerMixin {
String toString() { String toString() {
return 'AppState{\nuser: ${user}\nisLoading: $isLoading\nisLoaded: $isLoaded\napiKey: ${ellipse( return 'AppState{\nuser: ${user}\nisLoading: $isLoading\nisLoaded: $isLoaded\napiKey: ${ellipse(
firesApiKey, 8)}\napiUrl: ${ellipse( firesApiKey, 8)}\napiUrl: ${ellipse(
firesApiUrl, 8)}\nyourLocations count: ${yourLocations firesApiUrl, 8)}\nserverUrl: ${serverUrl}\nfireNotifications: ${fireNotifications}\nyourLocations count: ${yourLocations
.length}\nyourLocations: ${yourLocations}\nfireNotifications: ${fireNotifications}\nfireMapState: $fireMapState}'; .length}\nunread notif: ${fireNotificationsUnread}\nyourLocations: ${yourLocations}\nfireMapState: $fireMapState}';
} }
} }

View file

@ -1,40 +1,53 @@
import 'package:bson_objectid/bson_objectid.dart'; import 'package:bson_objectid/bson_objectid.dart';
import 'package:comunes_flutter/comunes_flutter.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:json_annotation/json_annotation.dart'; import 'package:json_annotation/json_annotation.dart';
import 'package:redux/src/store.dart';
import '../genericMap.dart';
import '../objectIdUtils.dart';
import '../redux/actions.dart';
part 'fireNotification.g.dart'; part 'fireNotification.g.dart';
_objectIdFromJson(String json) {
return new ObjectId.fromHexString(json);
}
_objectIdToJson(ObjectId o) {
return o.toString();
}
@JsonSerializable(nullable: false) @JsonSerializable(nullable: false)
class FireNotification extends Object with _$FireNotificationSerializerMixin { class FireNotification extends Object with _$FireNotificationSerializerMixin {
@JsonKey(toJson: _objectIdToJson, fromJson: _objectIdFromJson) @JsonKey(toJson: objectIdToJson, fromJson: objectIdFromJson)
ObjectId id; ObjectId id;
final double lat; final double lat;
final double lon; final double lon;
final String description; final String description;
final DateTime when; final DateTime when;
final String sealed;
@JsonKey(toJson: objectIdToJson, fromJson: objectIdFromJson)
final ObjectId subsId;
final bool read; final bool read;
factory FireNotification.fromJson(Map<String, dynamic> json) => _$FireNotificationFromJson(json); factory FireNotification.fromJson(Map<String, dynamic> json) =>
_$FireNotificationFromJson(json);
FireNotification({this.id, @required this.lat, @required this.lon, @required this.description, @required this.when, @required this.read}) { FireNotification(
{this.id,
@required this.lat,
@required this.lon,
@required this.description,
@required this.when,
@required this.read,
@required this.sealed,
@required this.subsId}) {
if (this.id == null) this.id = new ObjectId(); if (this.id == null) this.id = new ObjectId();
} }
FireNotification copyWith({id, lat, lon, description, read, when}) { FireNotification copyWith(
{id, lat, lon, description, read, when, sealed, subsId}) {
return new FireNotification( return new FireNotification(
id: id ?? this.id, id: id ?? this.id,
lat: lat ?? this.lat, lat: lat ?? this.lat,
lon: lon ?? this.lon, lon: lon ?? this.lon,
read: read ?? this.read, read: read ?? this.read,
description: description ?? this.description, description: description ?? this.description,
sealed: sealed ?? this.sealed,
subsId: subsId ?? this.subsId,
when: when ?? this.when); when: when ?? this.when);
} }
@ -48,13 +61,43 @@ class FireNotification extends Object with _$FireNotificationSerializerMixin {
lon == other.lon && lon == other.lon &&
read == other.read && read == other.read &&
description == other.description && description == other.description &&
sealed == other.sealed &&
subsId == other.subsId &&
when == other.when; when == other.when;
@override @override
int get hashCode => id.hashCode ^ lat.hashCode ^ lon.hashCode ^ description.hashCode ^ when.hashCode ^ read.hashCode; int get hashCode =>
id.hashCode ^
lat.hashCode ^
lon.hashCode ^
description.hashCode ^
when.hashCode ^
read.hashCode ^
sealed.hashCode ^
subsId.hashCode;
@override @override
String toString() { String toString() {
return 'FireNotification {id: $id, lat: $lat, lon: $lon, description: $description, when: $when, read: $read}'; 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>>{};
Route<Null> getRoute(Store store) {
final String routeName = '/fire/${id}';
return routes.putIfAbsent(
routeName,
() => new MaterialPageRoute<Null>(
settings: new RouteSettings(name: routeName),
builder: (BuildContext context) {
store.dispatch(new ShowFireNotificationMapAction(this));
Navigator.push(
context,
new MaterialPageRoute(
builder: (context) => new genericMap()));
},
),
);
} }
} }

View file

@ -10,12 +10,14 @@ part of 'fireNotification.dart';
FireNotification _$FireNotificationFromJson(Map<String, dynamic> json) => FireNotification _$FireNotificationFromJson(Map<String, dynamic> json) =>
new FireNotification( new FireNotification(
id: _objectIdFromJson(json['id'] as String), id: objectIdFromJson(json['id'] as String),
lat: (json['lat'] as num).toDouble(), lat: (json['lat'] as num).toDouble(),
lon: (json['lon'] as num).toDouble(), lon: (json['lon'] as num).toDouble(),
description: json['description'] as String, description: json['description'] as String,
when: DateTime.parse(json['when'] as String), when: DateTime.parse(json['when'] as String),
read: json['read'] as bool); read: json['read'] as bool,
sealed: json['sealed'] as String,
subsId: objectIdFromJson(json['subsId'] as String));
abstract class _$FireNotificationSerializerMixin { abstract class _$FireNotificationSerializerMixin {
ObjectId get id; ObjectId get id;
@ -23,6 +25,8 @@ abstract class _$FireNotificationSerializerMixin {
double get lon; double get lon;
String get description; String get description;
DateTime get when; DateTime get when;
String get sealed;
ObjectId get subsId;
bool get read; bool get read;
Map<String, dynamic> toJson() => new _$FireNotificationJsonMapWrapper(this); Map<String, dynamic> toJson() => new _$FireNotificationJsonMapWrapper(this);
} }
@ -32,15 +36,23 @@ class _$FireNotificationJsonMapWrapper extends $JsonMapWrapper {
_$FireNotificationJsonMapWrapper(this._v); _$FireNotificationJsonMapWrapper(this._v);
@override @override
Iterable<String> get keys => Iterable<String> get keys => const [
const ['id', 'lat', 'lon', 'description', 'when', 'read']; 'id',
'lat',
'lon',
'description',
'when',
'sealed',
'subsId',
'read'
];
@override @override
dynamic operator [](Object key) { dynamic operator [](Object key) {
if (key is String) { if (key is String) {
switch (key) { switch (key) {
case 'id': case 'id':
return _objectIdToJson(_v.id); return objectIdToJson(_v.id);
case 'lat': case 'lat':
return _v.lat; return _v.lat;
case 'lon': case 'lon':
@ -49,6 +61,10 @@ class _$FireNotificationJsonMapWrapper extends $JsonMapWrapper {
return _v.description; return _v.description;
case 'when': case 'when':
return _v.when.toIso8601String(); return _v.when.toIso8601String();
case 'sealed':
return _v.sealed;
case 'subsId':
return objectIdToJson(_v.subsId);
case 'read': case 'read':
return _v.read; return _v.read;
} }

View file

@ -1,20 +1,15 @@
import 'package:bson_objectid/bson_objectid.dart'; import 'package:bson_objectid/bson_objectid.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:json_annotation/json_annotation.dart'; import 'package:json_annotation/json_annotation.dart';
import 'package:fires_flutter/objectIdUtils.dart';
part 'yourLocation.g.dart'; part 'yourLocation.g.dart';
_objectIdFromJson(String json) {
return new ObjectId.fromHexString(json);
}
_objectIdToJson(ObjectId o) {
return o.toString();
}
@JsonSerializable(nullable: false) @JsonSerializable(nullable: false)
class YourLocation extends Object with _$YourLocationSerializerMixin { class YourLocation extends Object with _$YourLocationSerializerMixin {
@JsonKey(toJson: _objectIdToJson, fromJson: _objectIdFromJson) @JsonKey(toJson: objectIdToJson, fromJson: objectIdFromJson)
ObjectId id; ObjectId id;
final double lat; final double lat;
final double lon; final double lon;

View file

@ -10,7 +10,7 @@ part of 'yourLocation.dart';
YourLocation _$YourLocationFromJson(Map<String, dynamic> json) => YourLocation _$YourLocationFromJson(Map<String, dynamic> json) =>
new YourLocation( new YourLocation(
id: _objectIdFromJson(json['id'] as String), id: objectIdFromJson(json['id'] as String),
lat: (json['lat'] as num).toDouble(), lat: (json['lat'] as num).toDouble(),
lon: (json['lon'] as num).toDouble(), lon: (json['lon'] as num).toDouble(),
description: json['description'] as String, description: json['description'] as String,
@ -40,7 +40,7 @@ class _$YourLocationJsonMapWrapper extends $JsonMapWrapper {
if (key is String) { if (key is String) {
switch (key) { switch (key) {
case 'id': case 'id':
return _objectIdToJson(_v.id); return objectIdToJson(_v.id);
case 'lat': case 'lat':
return _v.lat; return _v.lat;
case 'lon': case 'lon':

9
lib/objectIdUtils.dart Normal file
View file

@ -0,0 +1,9 @@
import 'package:bson_objectid/bson_objectid.dart';
objectIdFromJson(String json) {
return new ObjectId.fromHexString(json);
}
objectIdToJson(ObjectId o) {
return o.toString();
}

View file

@ -41,6 +41,7 @@ class OnUserLangAction extends AppActions {
class FetchFireNotificationsSucceededAction extends AppActions { class FetchFireNotificationsSucceededAction extends AppActions {
final List<FireNotification> fetchedFireNotifications; final List<FireNotification> fetchedFireNotifications;
final int unreadCount;
FetchFireNotificationsSucceededAction(this.fetchedFireNotifications); FetchFireNotificationsSucceededAction(this.fetchedFireNotifications, this.unreadCount);
} }

View file

@ -6,7 +6,16 @@ AppState appReducer(AppState state, action) {
return state.copyWith(yourLocations: action.fetchedYourLocations); return state.copyWith(yourLocations: action.fetchedYourLocations);
} }
if (action is FetchFireNotificationsSucceededAction) { if (action is FetchFireNotificationsSucceededAction) {
return state.copyWith(fireNotifications: action.fetchedFireNotifications); return state.copyWith(fireNotifications: action.fetchedFireNotifications,
fireNotificationsUnread: action.unreadCount);
} }
if (action is AddedFireNotificationAction)
return state.copyWith(
fireNotificationsUnread: state.fireNotificationsUnread + 1);
if (action is ReadedFireNotificationAction)
return state.copyWith(
fireNotificationsUnread: state.fireNotificationsUnread - 1);
return state; return state;
} }

View file

@ -1,5 +1,6 @@
import 'package:bson_objectid/bson_objectid.dart'; import 'package:bson_objectid/bson_objectid.dart';
import 'package:fires_flutter/models/yourLocation.dart'; import 'package:fires_flutter/models/yourLocation.dart';
import 'package:fires_flutter/models/fireNotification.dart';
import 'package:flutter_simple_dependency_injection/injector.dart'; import 'package:flutter_simple_dependency_injection/injector.dart';
import 'package:just_debounce_it/just_debounce_it.dart'; import 'package:just_debounce_it/just_debounce_it.dart';
import 'package:redux/redux.dart'; import 'package:redux/redux.dart';
@ -161,8 +162,12 @@ void fetchDataMiddleware(Store<AppState> store, action, NextDispatcher next) {
if (action is FetchFireNotificationsAction) { if (action is FetchFireNotificationsAction) {
loadFireNotifications().then((fireNotifications) { loadFireNotifications().then((fireNotifications) {
int unread = 0;
for (FireNotification notif in fireNotifications) {
if (!notif.read) { unread++; }
}
store.dispatch( store.dispatch(
new FetchFireNotificationsSucceededAction(fireNotifications)); new FetchFireNotificationsSucceededAction(fireNotifications, unread));
persistFireNotifications(fireNotifications); persistFireNotifications(fireNotifications);
}); });
} }

View file

@ -44,7 +44,7 @@ FireMapState _showYourLocationMap(
status: action.loc.subscribed status: action.loc.subscribed
? FireMapStatus.unsubscribe ? FireMapStatus.unsubscribe
: FireMapStatus.view, : FireMapStatus.view,
yourLocation: action.loc); yourLocation: action.loc, fireNotication: null);
} }
FireMapState _showFireNotificationMap( FireMapState _showFireNotificationMap(

View file

@ -16,7 +16,7 @@ final fireNotificationReducer = combineReducers<List<FireNotification>>([
List<FireNotification> _receivedFireNotification( List<FireNotification> _receivedFireNotification(
List<FireNotification> notifications, AddedFireNotificationAction action) { List<FireNotification> notifications, AddedFireNotificationAction action) {
return new List.from(notifications)..add(action.notif); return new List.from(notifications)..insert(0, action.notif);
} }
List<FireNotification> _deletedFireNotification( List<FireNotification> _deletedFireNotification(

View file

@ -1,22 +1,21 @@
import '../models/appState.dart'; import '../models/appState.dart';
import 'appReducer.dart';
import 'errorReducer.dart'; import 'errorReducer.dart';
import 'fireMapReducer.dart'; import 'fireMapReducer.dart';
import 'fireNotificationReducer.dart';
import 'loadedReducer.dart'; import 'loadedReducer.dart';
import 'loadingReducer.dart'; import 'loadingReducer.dart';
import 'userReducer.dart'; import 'userReducer.dart';
import 'yourLocationsReducer.dart'; import 'yourLocationsReducer.dart';
import 'actions.dart';
import 'appReducer.dart';
import 'fireNotificationReducer.dart';
// We create the State reducer by combining many smaller reducers into one! // We create the State reducer by combining many smaller reducers into one!
AppState appStateReducer(AppState prevState, action) { AppState appStateReducer(AppState prevState, action) {
var state = prevState; var state = appReducer(prevState, action);
if (action is AppActions)
state = appReducer(prevState, action);
return AppState( return AppState(
yourLocations: yourLocationsReducer(state.yourLocations, action), yourLocations: yourLocationsReducer(state.yourLocations, action),
fireNotifications: fireNotificationReducer(state.fireNotifications, action), fireNotifications:
fireNotificationReducer(state.fireNotifications, action),
fireNotificationsUnread: state.fireNotificationsUnread,
user: userReducer(state.user, action), user: userReducer(state.user, action),
isLoading: loadingReducer(state.isLoading, action), isLoading: loadingReducer(state.isLoading, action),
isLoaded: loadedReducer(state.isLoaded, action), isLoaded: loadedReducer(state.isLoaded, action),
@ -24,6 +23,6 @@ AppState appStateReducer(AppState prevState, action) {
fireMapState: fireMapReducer(state.fireMapState, action), fireMapState: fireMapReducer(state.fireMapState, action),
firesApiKey: state.firesApiKey, firesApiKey: state.firesApiKey,
firesApiUrl: state.firesApiUrl, firesApiUrl: state.firesApiUrl,
gmapKey: state.gmapKey serverUrl: state.serverUrl,
); gmapKey: state.gmapKey);
} }

View file

@ -50,6 +50,8 @@ dependencies:
# https://pub.dartlang.org/packages/padder # https://pub.dartlang.org/packages/padder
padder: "^1.0.1" padder: "^1.0.1"
flutter_fab_dialer: "^0.0.5" flutter_fab_dialer: "^0.0.5"
badge: "^0.0.2"
flutter_spinkit: "^1.0.0"
# firebase # firebase
firebase_messaging: "^1.0.4" firebase_messaging: "^1.0.4"

View file

@ -59,5 +59,7 @@
"fireNotificationsDescription": "Here you will receive fire notifications in locations you are subscribed to", "fireNotificationsDescription": "Here you will receive fire notifications in locations you are subscribed to",
"areYouSureTitle": "Are you sure?", "areYouSureTitle": "Are you sure?",
"deleteAllFireNotificationsAlertDescription": "This will remove all your fire notifications", "deleteAllFireNotificationsAlertDescription": "This will remove all your fire notifications",
"DELETE": "DELETE" "DELETE": "DELETE",
"SHOW": "SHOW",
"CLOSE": "CLOSE"
} }

View file

@ -59,5 +59,7 @@
"fireNotificationsDescription": "Aquí recibirás las notificaciones de fuegos en los lugares a los que te subscribas", "fireNotificationsDescription": "Aquí recibirás las notificaciones de fuegos en los lugares a los que te subscribas",
"areYouSureTitle": "¿Seguro?", "areYouSureTitle": "¿Seguro?",
"deleteAllFireNotificationsAlertDescription": "Esto borrará todas las notificaciones de fuegos", "deleteAllFireNotificationsAlertDescription": "Esto borrará todas las notificaciones de fuegos",
"DELETE": "BORRAR" "DELETE": "BORRAR",
"SHOW": "MOSTRAR",
"CLOSE": "CERRAR"
} }