Fire notifications

This commit is contained in:
Vicente J. Ruiz Jurado 2018-07-26 19:13:12 +02:00
parent c680482b08
commit 5ecc3c3401
22 changed files with 524 additions and 31 deletions

View file

@ -80,15 +80,6 @@ class _FireAlertState extends State<FireAlert> {
buildTweetButton()
])),
]);
final stepInc = () {
setState(() {
if (_currentStep < fireSteps.length - 1) {
_currentStep += 1;
} else {
_currentStep = 0;
}
});
};
return Scaffold(
key: _scaffoldKey,
appBar: new AppBar(title: new Text(S.of(context).notifyAFire)),

View file

@ -0,0 +1,194 @@
import 'dart:async';
import 'package:comunes_flutter/comunes_flutter.dart';
import 'package:fires_flutter/models/fireNotification.dart';
import 'package:flutter/material.dart';
import 'package:flutter_redux/flutter_redux.dart';
import 'package:redux/src/store.dart';
import 'customMoment.dart';
import 'generated/i18n.dart';
import 'mainDrawer.dart';
import 'models/appState.dart';
import 'redux/actions.dart';
// import 'fireNotificationMap.dart';
@immutable
class _ViewModel {
final List<FireNotification> fireNotifications;
final TapFireNotificationFunction onTap;
//final OnReceivedFireNotificationFunction onReceived;
final DeleteFireNotificationFunction onDelete;
final DeleteAllFireNotificationFunction onDeleteAll;
_ViewModel(
{@required this.onTap,
// @required this.onReceived,
@required this.onDelete,
@required this.onDeleteAll,
@required this.fireNotifications});
@override
bool operator ==(Object other) {
return identical(this, other) ||
other is _ViewModel &&
runtimeType == other.runtimeType &&
fireNotifications == other.fireNotifications;
}
@override
int get hashCode => fireNotifications.hashCode;
}
class FireNotificationList extends StatefulWidget {
static const String routeName = '/fireNotifications';
@override
_FireNotificationListState createState() => _FireNotificationListState();
}
class _FireNotificationListState extends State<FireNotificationList> {
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
Widget _buildRow(
BuildContext context, FireNotification notif, onDeleted, onTap) {
return new ListTile(
dense: true,
leading: const Icon(Icons.whatshot),
title: new Text(notif.description),
subtitle: new Text(Moment.now().from(context, notif.when)),
onLongPress: () {
showSnackMsg(S.of(context).toDeleteThisPlace);
},
onTap: () {
onTap(notif);
});
}
void showSnackMsg(String msg) {
_scaffoldKey.currentState.showSnackBar(new SnackBar(
content: new Text(msg),
));
}
Widget _buildSavedFireNotifications(BuildContext context,
List<FireNotification> notifList, onDeleted, onTap) {
return new RefreshIndicator(
child: new ListView.builder(
padding: const EdgeInsets.all(16.0),
itemCount: notifList.length,
itemBuilder: (BuildContext _context, int i) {
final ThemeData theme = Theme.of(context);
return new Dismissible(
key: new ObjectKey(notifList.elementAt(i)),
direction: DismissDirection.horizontal,
onDismissed: (DismissDirection direction) {
onDeleted(notifList.elementAt(i));
},
background: new Container(
color: theme.primaryColor,
child: const ListTile(
leading: const Icon(Icons.delete,
color: Colors.white, size: 36.0))),
secondaryBackground: new Container(
color: theme.primaryColor,
child: const ListTile(
trailing: const Icon(Icons.delete,
color: Colors.white, size: 36.0))),
child: new Container(
decoration: new BoxDecoration(
color: theme.canvasColor,
border: new Border(
bottom:
new BorderSide(color: theme.dividerColor))),
child: _buildRow(
context, notifList.elementAt(i), onDeleted, onTap)));
}),
onRefresh: _handleRefresh);
}
Future<Null> _handleRefresh() async {
await new Future.delayed(new Duration(seconds: 1));
setState(() {});
return null;
}
@override
Widget build(BuildContext context) {
return new StoreConnector<AppState, _ViewModel>(
distinct: true,
converter: (store) {
print('New ViewModel of Fires Notifications');
return new _ViewModel(
onDeleteAll: () {
store.dispatch(new DeleteAllFireNotificationAction());
},
onDelete: (notif) {
store.dispatch(new DeleteFireNotificationAction(notif));
_scaffoldKey.currentState.showSnackBar(new SnackBar(
content: new Text(S.of(context).youDeletedThisPlace),
action: new SnackBarAction(
label: S.of(context).UNDO,
onPressed: () {
store.dispatch(new AddFireNotificationAction(notif));
})));
},
onTap: (notif) {},
fireNotifications: store.state.fireNotifications);
},
builder: (context, view) {
var hasFireNotifications = view.fireNotifications.length > 0;
final title = S.of(context).fireNotificationsTitle;
print('Building Fire Notifications List');
return Scaffold(
key: _scaffoldKey,
drawer: new MainDrawer(context),
appBar: new AppBar(
title: Text(title),
leading: IconButton(
icon: Icon(Icons.menu),
onPressed: () {
_scaffoldKey.currentState.openDrawer();
},
),
actions: listWithoutNulls(<Widget>[
hasFireNotifications
? IconButton(
icon: Icon(Icons.delete),
onPressed: () => view.onDeleteAll(),
)
: null
])),
body: !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.5, color: Colors.black45))
]))))
: _buildSavedFireNotifications(context,
view.fireNotifications, view.onDelete, view.onTap));
});
}
void gotoLocationMap(
Store<AppState> store, FireNotification notif, BuildContext context) {
/* store.dispatch(new ShowFireNotificationMapAction(notif));
Navigator.push(context,
new MaterialPageRoute(builder: (context) => new FireNotificationMap())); */
}
}

View file

@ -1,4 +1,6 @@
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';
@ -8,17 +10,36 @@ final FirebaseMessaging _firebaseMessaging = new FirebaseMessaging();
// https://pub.dartlang.org/packages/firebase_messaging#-example-tab-
void firebaseConfig(Store<AppState> store) {
_firebaseMessaging.configure(onLaunch: (Map<String, dynamic> message) {
print('On firebase launch');
}, onMessage: (Map<String, dynamic> message) {
print('On firebase message');
_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('On firebase resume');
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'],
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

@ -15,6 +15,7 @@ import 'theme.dart';
import 'privacyPage.dart';
import 'fireAlert.dart';
import 'supportPage.dart';
import 'fireNotificationList.dart';
class FiresApp extends StatelessWidget {
static final WidgetBuilder introWidget = (context) => new IntroPage();
@ -28,6 +29,7 @@ class FiresApp extends StatelessWidget {
Sandbox.routeName: (BuildContext context) => new Sandbox(),
FireAlert.routeName: (BuildContext context) => new FireAlert(),
SupportPage.routeName: (BuildContext context) => new SupportPage(),
FireNotificationList.routeName: (BuildContext context) => new FireNotificationList(),
};
final Store<AppState> store;

View file

@ -43,6 +43,8 @@ class S implements WidgetsLocalizations {
String get chooseAWatchRadio => "Choose a watch radio";
String get confirm => "Confirm";
String get errorFirePlaceDialog => "We couldn't get the location of the fire";
String get fireNotificationsDescription => "Here you will receive fire notifications in locations you are subscribed to";
String get fireNotificationsTitle => "Fire Notifications";
String get firesInTheWorld => "Active fires in the world";
String get firesInYourPlaces => "Active fires in your places";
String get firesNearPlace => "Fires near other place";
@ -91,6 +93,8 @@ class es extends S {
@override
String get alertWhenThereIsAFire => "Alerta cuando hay un fuego";
@override
String get fireNotificationsDescription => "Aquí recibirás las notificaciones de fuegos en los lugares a los que te subscribas";
@override
String get anHour => "una hora";
@override
String get notifyAFire => "Notificar un fuego";
@ -131,6 +135,8 @@ class es extends S {
@override
String get aMinute => "un minuto";
@override
String get fireNotificationsTitle => "Notificaciones de fuegos";
@override
String get firesInYourPlaces => "Fuegos en tus lugares";
@override
String get isYourUbicationEnabled => "No podemos saber tu ubicación actual. ¿Están los servicios de ubicación en tu móvil activados?";

View file

@ -29,7 +29,7 @@ void mainCommon(List<Middleware<AppState>> otherMiddleware) {
firesApiKey: secrets['firesApiKey'],
firesApiUrl: secrets['firesApiUrl'] + "api/v1/"),
middleware: List.from(otherMiddleware)
..add(fetchYourLocationsMiddleware));
..add(fetchDataMiddleware));
injector.map<String>((i) => store.state.firesApiUrl, key: "firesApiUrl");
injector.map<String>((i) => store.state.firesApiKey, key: "firesApiKey");

View file

@ -9,6 +9,8 @@ import 'package:comunes_flutter/comunes_flutter.dart';
import 'privacyPage.dart';
import 'fireAlert.dart';
import 'supportPage.dart';
import 'fireNotificationList.dart';
import 'package:community_material_icon/community_material_icon.dart';
class MainDrawer extends Drawer {
MainDrawer(BuildContext context, {key})
@ -63,6 +65,15 @@ Widget mainDrawer(BuildContext context) {
Navigator.pushNamed(context, FireAlert.routeName);
},
),
new ListTile(
leading: const Icon(Icons.notifications),
title: new Text(S.of(context).fireNotificationsTitle),
onTap: () {
// Then close the drawer
Navigator.pop(context);
Navigator.pushNamed(context, FireNotificationList.routeName);
},
),
new Divider(),
new ListTile(
leading: const Icon(Icons.favorite),

View file

@ -1,5 +1,6 @@
import 'package:comunes_flutter/comunes_flutter.dart';
import 'package:fires_flutter/models/yourLocation.dart';
import 'package:fires_flutter/models/fireNotification.dart';
import 'package:json_annotation/json_annotation.dart';
import 'package:meta/meta.dart';
@ -28,6 +29,7 @@ class AppState extends Object with _$AppStateSerializerMixin {
@JsonKey(ignore: true)
final String firesApiUrl;
final List<YourLocation> yourLocations;
final List<FireNotification> fireNotifications;
@JsonKey(ignore: true)
final FireMapState fireMapState;
@ -37,6 +39,7 @@ class AppState extends Object with _$AppStateSerializerMixin {
AppState(
{this.yourLocations: const <YourLocation>[],
this.fireNotifications: const <FireNotification>[],
this.user: const User.initial(),
this.isLoading: false,
this.isLoaded: false,
@ -55,6 +58,7 @@ class AppState extends Object with _$AppStateSerializerMixin {
String firesApiKey,
String firesApiUrl,
List<YourLocation> yourLocations,
List<FireNotification> fireNotifications,
FireMapState fireMapState}) {
return new AppState(
isLoading: isLoading ?? this.isLoading,
@ -65,6 +69,7 @@ class AppState extends Object with _$AppStateSerializerMixin {
firesApiKey: firesApiKey ?? this.firesApiKey,
firesApiUrl: firesApiUrl ?? this.firesApiUrl,
yourLocations: yourLocations ?? this.yourLocations,
fireNotifications: fireNotifications ?? this.fireNotifications,
fireMapState: fireMapState ?? this.fireMapState);
}
@ -73,24 +78,26 @@ class AppState extends Object with _$AppStateSerializerMixin {
return 'AppState{\nuser: ${user}\nisLoading: $isLoading\nisLoaded: $isLoaded\napiKey: ${ellipse(
firesApiKey, 8)}\napiUrl: ${ellipse(
firesApiUrl, 8)}\nyourLocations count: ${yourLocations
.length}\nyourLocations: ${yourLocations}\nfireMapState: $fireMapState}';
.length}\nyourLocations: ${yourLocations}\nfireNotifications: ${fireNotifications}\nfireMapState: $fireMapState}';
}
}
typedef void AddYourLocationFunction(YourLocation loc);
typedef void DeleteYourLocationFunction(YourLocation loc);
typedef void ToggleSubscriptionFunction(YourLocation loc);
typedef void OnLocationTapFunction(YourLocation loc);
typedef void OnSubscribeFunction(YourLocation loc);
typedef void OnSubscribeDistanceChangeFunction(YourLocation loc);
typedef void OnUnSubscribeFunction(YourLocation loc);
typedef void OnSubscribeConfirmedFunction(YourLocation loc);
typedef void OnLocationEdit(YourLocation loc);
typedef void OnLocationEditing(YourLocation loc);
typedef void OnLocationEditConfirm(YourLocation loc);
typedef void OnLocationEditCancel(YourLocation loc);
typedef void TapFireNotificationFunction(FireNotification notif);
// typedef void OnReceivedFireNotificationFunction(FireNotification notif);
typedef void DeleteFireNotificationFunction(FireNotification notif);
typedef void DeleteAllFireNotificationFunction();
// unused
// typedef void UpdateYourLocationFunction(ObjectId id, YourLocation loc);

View file

@ -11,10 +11,14 @@ part of 'appState.dart';
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);
}
@ -23,7 +27,7 @@ class _$AppStateJsonMapWrapper extends $JsonMapWrapper {
_$AppStateJsonMapWrapper(this._v);
@override
Iterable<String> get keys => const ['yourLocations'];
Iterable<String> get keys => const ['yourLocations', 'fireNotifications'];
@override
dynamic operator [](Object key) {
@ -31,6 +35,8 @@ class _$AppStateJsonMapWrapper extends $JsonMapWrapper {
switch (key) {
case 'yourLocations':
return _v.yourLocations;
case 'fireNotifications':
return _v.fireNotifications;
}
}
return null;

View file

@ -0,0 +1,59 @@
import 'package:bson_objectid/bson_objectid.dart';
import 'package:flutter/material.dart';
import 'package:json_annotation/json_annotation.dart';
part 'fireNotification.g.dart';
_objectIdFromJson(String json) {
return new ObjectId.fromHexString(json);
}
_objectIdToJson(ObjectId o) {
return o.toString();
}
@JsonSerializable(nullable: false)
class FireNotification extends Object with _$FireNotificationSerializerMixin {
@JsonKey(toJson: _objectIdToJson, fromJson: _objectIdFromJson)
ObjectId id;
final double lat;
final double lon;
final String description;
final DateTime when;
factory FireNotification.fromJson(Map<String, dynamic> json) => _$FireNotificationFromJson(json);
// static FireNotification noLocation = new FireNotification(lat: 0.0, lon: 0.0, description: '', when: new DateTime(0));
FireNotification({this.id, @required this.lat, @required this.lon, @required this.description, @required this.when}) {
if (this.id == null) this.id = new ObjectId();
}
FireNotification copyWith({id, lat, lon, description, when}) {
return new FireNotification(
id: id ?? this.id,
lat: lat ?? this.lat,
lon: lon ?? this.lon,
description: description ?? this.description,
when: when ?? this.when);
}
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is FireNotification &&
runtimeType == other.runtimeType &&
id == other.id &&
lat == other.lat &&
lon == other.lon &&
description == other.description &&
when == other.when;
@override
int get hashCode => id.hashCode ^ lat.hashCode ^ lon.hashCode ^ description.hashCode ^ when.hashCode;
@override
String toString() {
return 'FireNotification {id: $id, lat: $lat, lon: $lon, description: $description, when: $when}';
}
}

View file

@ -0,0 +1,54 @@
// Copyright (c) 2018, Comunes Association.
// GENERATED CODE - DO NOT MODIFY BY HAND
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));
abstract class _$FireNotificationSerializerMixin {
ObjectId get id;
double get lat;
double get lon;
String get description;
DateTime get when;
Map<String, dynamic> toJson() => new _$FireNotificationJsonMapWrapper(this);
}
class _$FireNotificationJsonMapWrapper extends $JsonMapWrapper {
final _$FireNotificationSerializerMixin _v;
_$FireNotificationJsonMapWrapper(this._v);
@override
Iterable<String> get keys =>
const ['id', 'lat', 'lon', 'description', 'when'];
@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();
}
}
return null;
}
}

View file

@ -0,0 +1,36 @@
import 'dart:async';
import 'dart:convert';
import 'package:fires_flutter/models/fireNotification.dart';
import '../globals.dart' as globals;
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>();
if (FireNotifications == null) {
FireNotifications = [];
// first run, init with empty list
persistFireNotifications(persistedList);
}
FireNotifications.forEach((notificationString) {
Map notificationMap = json.decode(notificationString);
persistedList.add(FireNotification.fromJson(notificationMap));
});
return persistedList;
});
}
persistFireNotifications(List<FireNotification> notif) {
print('Persisting $notif');
globals.prefs.then((prefs) {
List<String> notifAsString = [];
notif.forEach((notification) {
notifAsString.add(json.encode(notification.toJson()));
});
prefs.setStringList(fireNotificationKey, notifAsString);
});
}

View file

@ -1,3 +1,4 @@
export 'appActions.dart';
export 'yourLocationActions.dart';
export 'fireMapActions.dart';
export 'fireNotificationActions.dart';

View file

@ -1,4 +1,5 @@
import 'package:fires_flutter/models/yourLocation.dart';
import 'package:fires_flutter/models/fireNotification.dart';
abstract class AppActions {}
@ -12,6 +13,8 @@ class FetchYourLocationsSucceededAction extends AppActions {
FetchYourLocationsSucceededAction(this.fetchedYourLocations);
}
class FetchFireNotificationsAction extends AppActions {}
class FetchYourLocationsFailedAction extends AppActions {
final Exception error;
@ -35,3 +38,9 @@ class OnUserLangAction extends AppActions {
OnUserLangAction(this.lang);
}
class FetchFireNotificationsSucceededAction extends AppActions {
final List<FireNotification> fetchedFireNotifications;
FetchFireNotificationsSucceededAction(this.fetchedFireNotifications);
}

View file

@ -5,5 +5,8 @@ AppState appReducer(AppState state, action) {
if (action is FetchYourLocationsSucceededAction) {
return state.copyWith(yourLocations: action.fetchedYourLocations);
}
if (action is FetchFireNotificationsSucceededAction) {
return state.copyWith(fireNotifications: action.fetchedFireNotifications);
}
return state;
}

View file

@ -5,6 +5,7 @@ import 'package:just_debounce_it/just_debounce_it.dart';
import 'package:redux/redux.dart';
import '../models/appState.dart';
import '../models/fireNotificationsPersist.dart';
import '../models/firesApi.dart';
import '../models/yourLocationPersist.dart';
import 'actions.dart';
@ -20,8 +21,7 @@ import 'actions.dart';
FiresApi api = Injector.getInjector().get<FiresApi>();
void fetchYourLocationsMiddleware(
Store<AppState> store, action, NextDispatcher next) {
void fetchDataMiddleware(Store<AppState> store, action, NextDispatcher next) {
// If our Middleware encounters a `FetchYourLocationAction`
if (action is OnUserLangAction) {
@ -46,8 +46,7 @@ void fetchYourLocationsMiddleware(
if (action is AddYourLocationAction) {
if (action.loc.subscribed) {
subscribeViaApi(store, action.loc,
(sub) {
subscribeViaApi(store, action.loc, (sub) {
store.dispatch(new AddedYourLocationAction(sub));
persistYourLocations(store.state.yourLocations);
});
@ -69,6 +68,21 @@ void fetchYourLocationsMiddleware(
}
}
if (action is DeleteFireNotificationAction) {
store.dispatch(new DeletedFireNotificationAction(action.notif));
persistFireNotifications(store.state.fireNotifications);
}
if (action is DeleteAllFireNotificationAction) {
store.dispatch(new DeletedAllFireNotificationAction());
persistFireNotifications(store.state.fireNotifications);
}
if (action is AddFireNotificationAction) {
store.dispatch(new AddedFireNotificationAction(action.notif));
persistFireNotifications(store.state.fireNotifications);
}
if (action is ShowYourLocationMapAction) {
api
.getYourLocationFireStats(store.state, action.loc)
@ -139,6 +153,15 @@ void fetchYourLocationsMiddleware(
store.dispatch(new FetchYourLocationsFailedAction(error));
});
}
if (action is FetchFireNotificationsAction) {
loadFireNotifications().then((fireNotifications) {
store.dispatch(
new FetchFireNotificationsSucceededAction(fireNotifications));
persistFireNotifications(fireNotifications);
});
}
// Make sure our actions continue on to the reducer.
next(action);
}
@ -167,5 +190,6 @@ void createUser(store, lang, token) {
api.createUser(store.state, token, lang).then((userId) {
store.dispatch(new OnUserCreatedAction(userId));
store.dispatch(new FetchYourLocationsAction());
store.dispatch(new FetchFireNotificationsAction());
});
}

View file

@ -0,0 +1,35 @@
import 'package:fires_flutter/models/fireNotification.dart';
abstract class FireNotificationActions {}
class DeleteFireNotificationAction extends FireNotificationActions {
final FireNotification notif;
DeleteFireNotificationAction(this.notif);
}
class DeleteAllFireNotificationAction extends FireNotificationActions {
DeleteAllFireNotificationAction();
}
class AddFireNotificationAction extends FireNotificationActions {
final FireNotification notif;
AddFireNotificationAction(this.notif);
}
class DeletedFireNotificationAction extends FireNotificationActions {
final FireNotification notif;
DeletedFireNotificationAction(this.notif);
}
class DeletedAllFireNotificationAction extends FireNotificationActions {
DeletedAllFireNotificationAction();
}
class AddedFireNotificationAction extends FireNotificationActions {
final FireNotification notif;
AddedFireNotificationAction(this.notif);
}

View file

@ -0,0 +1,28 @@
import 'package:redux/redux.dart';
import 'actions.dart';
import 'package:fires_flutter/models/fireNotification.dart';
final fireNotificationReducer = combineReducers<List<FireNotification>>([
new TypedReducer<List<FireNotification>, AddedFireNotificationAction>(
_receivedFireNotification),
new TypedReducer<List<FireNotification>, DeletedFireNotificationAction>(
_deletedFireNotification),
new TypedReducer<List<FireNotification>, DeletedAllFireNotificationAction>(
_deletedAllFireNotifications)
]);
List<FireNotification> _receivedFireNotification(
List<FireNotification> yourLocations, AddedFireNotificationAction action) {
return new List.from(yourLocations)..add(action.notif);
}
List<FireNotification> _deletedFireNotification(
List<FireNotification> notifications, DeletedFireNotificationAction action) {
return new List.from(notifications)..remove(action.notif);
}
List<FireNotification> _deletedAllFireNotifications(
List<FireNotification> notifications, DeletedAllFireNotificationAction action) {
return new List<FireNotification>();
}

View file

@ -7,6 +7,7 @@ import 'userReducer.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!
AppState appStateReducer(AppState prevState, action) {
@ -15,6 +16,7 @@ AppState appStateReducer(AppState prevState, action) {
state = appReducer(prevState, action);
return AppState(
yourLocations: yourLocationsReducer(state.yourLocations, action),
fireNotifications: fireNotificationReducer(state.fireNotifications, action),
user: userReducer(state.user, action),
isLoading: loadingReducer(state.isLoading, action),
isLoaded: loadedReducer(state.isLoaded, action),

View file

@ -52,7 +52,7 @@ dependencies:
flutter_fab_dialer: "^0.0.5"
# firebase
firebase_messaging: "^1.0.2"
firebase_messaging: "^1.0.4"
# icons
# The following adds the Cupertino Icons font to your application.

View file

@ -51,5 +51,7 @@
"callEmergencyServicesDescription": "You can call 112 if you have not already done so to notify the emergency services.",
"tweetAboutAFireTitle": "Tweet about",
"tweetAboutAFireDescription": "Additionally if you use twitter you can share additional information with the emergency services, for example, attaching photos to the tweet if you have good visibility of the fire, when you took the photos, exact location, etc. Use #hashtags type #IFMinicipalTerminal for example #IFJumilla (Forest Fire in Jumilla).",
"supportPageDescription": "You can support this initiative, spreading the word, helping us to develop our software, translating this app to your language, etc."
"supportPageDescription": "You can support this initiative, spreading the word, helping us to develop our software, translating this app to your language, etc.",
"fireNotificationsTitle": "Fire Notifications",
"fireNotificationsDescription": "Here you will receive fire notifications in locations you are subscribed to"
}

View file

@ -51,5 +51,7 @@
"callEmergencyServicesDescription": "Puedes llamar al 112 si no lo has hecho ya para avisar a los servicios de emergencia.",
"tweetAboutAFireTitle": "Twittea",
"tweetAboutAFireDescription": "Adicionalmente si usas twitter puedes compartir información adicional con los servicios de emergencia, por ejemplo, adjuntando fotos al tweet si tienes buena visibilidad del fuego, cuando tomaste las fotos, ubicación exacta, etc. Usa #hashtags tipo #IFTerminoMunicipal por ejemplo #IFJumilla (Incendio Forestal en Jumilla).",
"supportPageDescription": "Puedes apoyar esta iniciativa, dando difusión, ayudándonos a desarrollar nuestro software, traduciendo esta aplicación a tu idioma, etc."
"supportPageDescription": "Puedes apoyar esta iniciativa, dando difusión, ayudándonos a desarrollar nuestro software, traduciendo esta aplicación a tu idioma, etc.",
"fireNotificationsTitle": "Notificaciones de fuegos",
"fireNotificationsDescription": "Aquí recibirás las notificaciones de fuegos en los lugares a los que te subscribas"
}