More work with fire notifications
This commit is contained in:
parent
5fdaf239c0
commit
9dd8a7de97
24 changed files with 512 additions and 288 deletions
|
|
@ -5,17 +5,18 @@ 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:flutter_spinkit/flutter_spinkit.dart';
|
||||
import 'package:redux/src/store.dart';
|
||||
|
||||
import 'colors.dart';
|
||||
import 'generated/i18n.dart';
|
||||
import 'genericMap.dart';
|
||||
import 'globalFiresBottomStats.dart';
|
||||
import 'locationUtils.dart';
|
||||
import 'mainDrawer.dart';
|
||||
import 'models/appState.dart';
|
||||
import 'placesAutocompleteUtils.dart';
|
||||
import 'redux/actions.dart';
|
||||
import 'genericMap.dart';
|
||||
|
||||
@immutable
|
||||
class _ViewModel {
|
||||
|
|
@ -24,35 +25,36 @@ class _ViewModel {
|
|||
final DeleteYourLocationFunction onDelete;
|
||||
final ToggleSubscriptionFunction onToggleSubs;
|
||||
final OnLocationTapFunction onTap;
|
||||
final bool isLoading;
|
||||
|
||||
_ViewModel(
|
||||
{@required this.onAdd,
|
||||
@required this.onDelete,
|
||||
@required this.onToggleSubs,
|
||||
@required this.onTap,
|
||||
@required this.yourLocations});
|
||||
@required this.yourLocations,
|
||||
@required this.isLoading});
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is _ViewModel &&
|
||||
runtimeType == other.runtimeType &&
|
||||
yourLocations == other.yourLocations;
|
||||
}
|
||||
runtimeType == other.runtimeType &&
|
||||
yourLocations == other.yourLocations &&
|
||||
isLoading == other.isLoading;
|
||||
|
||||
@override
|
||||
int get hashCode => yourLocations.hashCode;
|
||||
int get hashCode => yourLocations.hashCode ^ isLoading.hashCode;
|
||||
}
|
||||
|
||||
class ActiveFiresPage extends StatefulWidget {
|
||||
static const String routeName = '/fires';
|
||||
static const String routeName = '/fires';
|
||||
|
||||
@override
|
||||
_ActiveFiresPageState createState() => _ActiveFiresPageState();
|
||||
}
|
||||
|
||||
class _ActiveFiresPageState extends State<ActiveFiresPage> {
|
||||
|
||||
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
|
||||
|
||||
List<FabMiniMenuItem> _fabMiniMenuItemList(
|
||||
|
|
@ -171,7 +173,8 @@ class _ActiveFiresPageState extends State<ActiveFiresPage> {
|
|||
onTap: (loc) {
|
||||
gotoLocationMap(store, loc, context);
|
||||
},
|
||||
yourLocations: store.state.yourLocations);
|
||||
yourLocations: store.state.yourLocations,
|
||||
isLoading: !store.state.isLoaded);
|
||||
},
|
||||
builder: (context, view) {
|
||||
var hasLocations = view.yourLocations.length > 0;
|
||||
|
|
@ -196,27 +199,29 @@ class _ActiveFiresPageState extends State<ActiveFiresPage> {
|
|||
floatingActionButtonLocation:
|
||||
FloatingActionButtonLocation.centerFloat,
|
||||
bottomNavigationBar: new GlobalFiresBottomStats(),
|
||||
body: hasLocations
|
||||
? new Stack(children: <Widget>[
|
||||
_buildSavedLocations(context, view.yourLocations,
|
||||
view.onDelete, view.onToggleSubs, view.onTap),
|
||||
new FabDialer(_fabMiniMenuItemList(context, view.onAdd),
|
||||
fires600, new Icon(Icons.add))
|
||||
])
|
||||
: new Center(
|
||||
child: new CenteredColumn(children: <Widget>[
|
||||
new RoundedBtn(
|
||||
icon: Icons.location_searching,
|
||||
text: S.of(context).addYourCurrentPosition,
|
||||
onPressed: () => onAddYourLocation(view.onAdd),
|
||||
backColor: fires600),
|
||||
const SizedBox(height: 26.0),
|
||||
new RoundedBtn(
|
||||
icon: Icons.edit_location,
|
||||
text: S.of(context).addSomePlace,
|
||||
onPressed: () => onAddOtherLocation(view.onAdd),
|
||||
backColor: fires600),
|
||||
])),
|
||||
body: view.isLoading
|
||||
? new SpinKitPulse(color: fires600)
|
||||
: hasLocations
|
||||
? new Stack(children: <Widget>[
|
||||
_buildSavedLocations(context, view.yourLocations,
|
||||
view.onDelete, view.onToggleSubs, view.onTap),
|
||||
new FabDialer(_fabMiniMenuItemList(context, view.onAdd),
|
||||
fires600, new Icon(Icons.add))
|
||||
])
|
||||
: new Center(
|
||||
child: new CenteredColumn(children: <Widget>[
|
||||
new RoundedBtn(
|
||||
icon: Icons.location_searching,
|
||||
text: S.of(context).addYourCurrentPosition,
|
||||
onPressed: () => onAddYourLocation(view.onAdd),
|
||||
backColor: fires600),
|
||||
const SizedBox(height: 26.0),
|
||||
new RoundedBtn(
|
||||
icon: Icons.edit_location,
|
||||
text: S.of(context).addSomePlace,
|
||||
onPressed: () => onAddOtherLocation(view.onAdd),
|
||||
backColor: fires600),
|
||||
])),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
|
@ -224,8 +229,8 @@ class _ActiveFiresPageState extends State<ActiveFiresPage> {
|
|||
void gotoLocationMap(
|
||||
Store<AppState> store, YourLocation loc, BuildContext context) {
|
||||
store.dispatch(new ShowYourLocationMapAction(loc));
|
||||
Navigator.push(context,
|
||||
new MaterialPageRoute(builder: (context) => new genericMap()));
|
||||
Navigator.push(
|
||||
context, new MaterialPageRoute(builder: (context) => new genericMap()));
|
||||
}
|
||||
|
||||
void onAddYourLocation(AddYourLocationFunction onAdd) {
|
||||
|
|
|
|||
|
|
@ -2,43 +2,49 @@ import 'dart:async';
|
|||
|
||||
import 'package:comunes_flutter/comunes_flutter.dart';
|
||||
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 'customMoment.dart';
|
||||
import 'generated/i18n.dart';
|
||||
import 'genericMap.dart';
|
||||
import 'mainDrawer.dart';
|
||||
import 'models/appState.dart';
|
||||
import 'redux/actions.dart';
|
||||
import 'genericMap.dart';
|
||||
|
||||
@immutable
|
||||
class _ViewModel {
|
||||
final List<FireNotification> fireNotifications;
|
||||
final int fireNotificationsUnread;
|
||||
final List<YourLocation> yourLocations;
|
||||
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});
|
||||
@required this.fireNotifications,
|
||||
@required this.yourLocations,
|
||||
@required this.fireNotificationsUnread});
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
other is _ViewModel &&
|
||||
runtimeType == other.runtimeType &&
|
||||
fireNotifications == other.fireNotifications;
|
||||
}
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is _ViewModel &&
|
||||
runtimeType == other.runtimeType &&
|
||||
fireNotifications == other.fireNotifications &&
|
||||
fireNotificationsUnread == other.fireNotificationsUnread &&
|
||||
yourLocations == other.yourLocations;
|
||||
|
||||
@override
|
||||
int get hashCode => fireNotifications.hashCode;
|
||||
int get hashCode =>
|
||||
fireNotifications.hashCode ^
|
||||
fireNotificationsUnread.hashCode ^
|
||||
yourLocations.hashCode;
|
||||
}
|
||||
|
||||
class FireNotificationList extends StatefulWidget {
|
||||
|
|
@ -51,12 +57,13 @@ class FireNotificationList extends StatefulWidget {
|
|||
class _FireNotificationListState extends State<FireNotificationList> {
|
||||
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
|
||||
|
||||
Widget _buildRow(
|
||||
BuildContext context, FireNotification notif, onDeleted, onTap) {
|
||||
Widget _buildRow(BuildContext context, List<YourLocation> yourLocations,
|
||||
FireNotification notif, onDeleted, onTap) {
|
||||
YourLocation yl = yourLocations.singleWhere((yl) => yl.id == notif.subsId);
|
||||
return new ListTile(
|
||||
dense: true,
|
||||
leading: const Icon(Icons.whatshot),
|
||||
title: new Text(notif.description,
|
||||
title: new Text('${yl.description}. ${notif.description}',
|
||||
style: new TextStyle(
|
||||
fontWeight: notif.read ? FontWeight.normal : FontWeight.bold)),
|
||||
subtitle: new Text(Moment.now().from(context, notif.when)),
|
||||
|
|
@ -74,13 +81,17 @@ class _FireNotificationListState extends State<FireNotificationList> {
|
|||
));
|
||||
}
|
||||
|
||||
Widget _buildSavedFireNotifications(BuildContext context,
|
||||
List<FireNotification> notifList, onDeleted, onTap) {
|
||||
Widget _buildSavedFireNotifications(
|
||||
BuildContext context,
|
||||
List<YourLocation> yourLocations,
|
||||
List<FireNotification> notifList,
|
||||
onDeleted,
|
||||
onTap) {
|
||||
return new RefreshIndicator(
|
||||
child: new ListView.builder(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
reverse: true,
|
||||
shrinkWrap: true,
|
||||
// reverse: true,
|
||||
// shrinkWrap: true,
|
||||
itemCount: notifList.length,
|
||||
itemBuilder: (BuildContext _context, int i) {
|
||||
final ThemeData theme = Theme.of(context);
|
||||
|
|
@ -106,8 +117,8 @@ class _FireNotificationListState extends State<FireNotificationList> {
|
|||
border: new Border(
|
||||
bottom:
|
||||
new BorderSide(color: theme.dividerColor))),
|
||||
child: _buildRow(
|
||||
context, notifList.elementAt(i), onDeleted, onTap)));
|
||||
child: _buildRow(context, yourLocations,
|
||||
notifList.elementAt(i), onDeleted, onTap)));
|
||||
}),
|
||||
onRefresh: _handleRefresh);
|
||||
}
|
||||
|
|
@ -145,11 +156,13 @@ class _FireNotificationListState extends State<FireNotificationList> {
|
|||
store.dispatch(new ReadFireNotificationAction(
|
||||
notif.copyWith(read: true)));
|
||||
}
|
||||
new Timer(new Duration(milliseconds: 1000), () {
|
||||
new Timer(new Duration(milliseconds: 500), () {
|
||||
gotoMap(store, notif, context);
|
||||
});
|
||||
},
|
||||
fireNotifications: store.state.fireNotifications);
|
||||
yourLocations: store.state.yourLocations,
|
||||
fireNotifications: store.state.fireNotifications,
|
||||
fireNotificationsUnread: store.state.fireNotificationsUnread);
|
||||
},
|
||||
builder: (context, view) {
|
||||
var hasFireNotifications = view.fireNotifications.length > 0;
|
||||
|
|
@ -191,16 +204,16 @@ class _FireNotificationListState extends State<FireNotificationList> {
|
|||
style: new TextStyle(
|
||||
height: 1.3, color: Colors.black45))
|
||||
]))))
|
||||
: _buildSavedFireNotifications(context,
|
||||
: _buildSavedFireNotifications(context, view.yourLocations,
|
||||
view.fireNotifications, view.onDelete, view.onTap));
|
||||
});
|
||||
}
|
||||
|
||||
void gotoMap(
|
||||
Store<AppState> store, FireNotification notif, BuildContext context) {
|
||||
store.dispatch(new ShowFireNotificationMapAction(notif));
|
||||
Navigator.push(context,
|
||||
new MaterialPageRoute(builder: (context) => new genericMap()));
|
||||
store.dispatch(new ShowFireNotificationMapAction(notif));
|
||||
Navigator.push(
|
||||
context, new MaterialPageRoute(builder: (context) => new genericMap()));
|
||||
}
|
||||
|
||||
_showConfirmDialog(_ViewModel view) {
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
|
|
@ -1,23 +1,38 @@
|
|||
import 'package:bson_objectid/bson_objectid.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_localizations/flutter_localizations.dart';
|
||||
import 'package:flutter_redux/flutter_redux.dart';
|
||||
import 'package:redux/redux.dart';
|
||||
import 'package:redux/src/store.dart';
|
||||
|
||||
import 'activeFires.dart';
|
||||
import 'fireAlert.dart';
|
||||
import 'fireNotificationList.dart';
|
||||
import 'generated/i18n.dart';
|
||||
import 'homePage.dart';
|
||||
import 'introPage.dart';
|
||||
import 'models/appState.dart';
|
||||
import 'privacyPage.dart';
|
||||
import 'redux/actions.dart';
|
||||
import 'sandbox.dart';
|
||||
import 'theme.dart';
|
||||
import 'privacyPage.dart';
|
||||
import 'fireAlert.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 continueWidget = (context) => new HomePage();
|
||||
|
||||
|
|
@ -29,22 +44,23 @@ 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(),
|
||||
FireNotificationList.routeName: (BuildContext context) =>
|
||||
new FireNotificationList(),
|
||||
};
|
||||
|
||||
final Store<AppState> store;
|
||||
|
||||
// globals.getIt.registerSingleton
|
||||
FiresApp(this.store);
|
||||
_FiresAppState(this.store);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
StatefulWidget home = new MaterialAppWithIntroHome(
|
||||
introWidget, continueWidget, 'showInitialWizard-2018-06-27-01');
|
||||
return new StoreProvider<AppState>(
|
||||
|
||||
store: this.store,
|
||||
child: new MaterialApp(
|
||||
navigatorKey: navigatorKey,
|
||||
localizationsDelegates: [
|
||||
S.delegate,
|
||||
GlobalMaterialLocalizations.delegate,
|
||||
|
|
@ -65,4 +81,97 @@ class FiresApp extends StatelessWidget {
|
|||
theme: firesTheme,
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,8 +23,10 @@ class S implements WidgetsLocalizations {
|
|||
|
||||
String get AvoidThisStringsIfisNotPlural => "Zero One Two Few Many Other";
|
||||
String get CANCEL => "CANCEL";
|
||||
String get CLOSE => "CLOSE";
|
||||
String get DELETE => "DELETE";
|
||||
String get SAVE => "SAVE";
|
||||
String get SHOW => "SHOW";
|
||||
String get UNDO => "UNDO";
|
||||
String get aDay => "a day";
|
||||
String get aF3wSeconds => "a few seconds";
|
||||
|
|
@ -111,6 +113,8 @@ class es extends S {
|
|||
@override
|
||||
String get callEmergencyServicesDescription => "Deberías llamar al 112 si no lo has hecho ya para avisar a los servicios de emergencia.";
|
||||
@override
|
||||
String get CLOSE => "CERRAR";
|
||||
@override
|
||||
String get errorFirePlaceDialog => "No hemos podido obtener la ubicación del fuego";
|
||||
@override
|
||||
String get toDeleteThisPlace => "Desliza horizontalmente para borrar este lugar";
|
||||
|
|
@ -163,6 +167,8 @@ class es extends S {
|
|||
@override
|
||||
String get SAVE => "GUARDAR";
|
||||
@override
|
||||
String get SHOW => "MOSTRAR";
|
||||
@override
|
||||
String get addedThisLocation => "Ya has añadido este lugar antes";
|
||||
@override
|
||||
String get typeTheNameOfAPlace => "Escribe el nombre de un lugar, zona, etc";
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import 'dart:core';
|
||||
|
||||
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/yourLocation.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_redux/flutter_redux.dart';
|
||||
import 'package:latlong/latlong.dart';
|
||||
import 'package:share/share.dart';
|
||||
|
||||
import 'colors.dart';
|
||||
import 'dummyMapPlugin.dart';
|
||||
|
|
@ -25,6 +25,7 @@ import 'zoomMapPlugin.dart';
|
|||
|
||||
@immutable
|
||||
class _ViewModel {
|
||||
final String serverUrl;
|
||||
final FireMapState mapState;
|
||||
final OnSubscribeFunction onSubs;
|
||||
final OnSubscribeConfirmedFunction onSubsConfirmed;
|
||||
|
|
@ -37,6 +38,7 @@ class _ViewModel {
|
|||
|
||||
_ViewModel(
|
||||
{@required this.mapState,
|
||||
@required this.serverUrl,
|
||||
@required this.onSubs,
|
||||
@required this.onSubsConfirmed,
|
||||
@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
|
||||
// https://github.com/flutter/flutter/issues/1632#issuecomment-180478202
|
||||
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
|
||||
final FirebaseMessaging _firebaseMessaging = new FirebaseMessaging();
|
||||
|
||||
YourLocation _location;
|
||||
YourLocation _initialLocation;
|
||||
|
||||
|
|
@ -105,6 +107,7 @@ class _genericMapState extends State<genericMap> {
|
|||
store.dispatch(new UpdateYourLocationMapAction(loc));
|
||||
store.dispatch(new EditConfirmYourLocationAction(loc));
|
||||
},
|
||||
serverUrl: store.state.firesApiUrl,
|
||||
mapState: store.state.fireMapState);
|
||||
},
|
||||
builder: (context, view) {
|
||||
|
|
@ -223,8 +226,6 @@ class _genericMapState extends State<genericMap> {
|
|||
break;
|
||||
case FireMapStatus.subscriptionConfirm:
|
||||
view.onSubsConfirmed(_location);
|
||||
// IOS specific
|
||||
_firebaseMessaging.requestNotificationPermissions();
|
||||
break;
|
||||
case FireMapStatus.unsubscribe:
|
||||
view.onUnSubs(_location);
|
||||
|
|
@ -299,6 +300,14 @@ class _genericMapState extends State<genericMap> {
|
|||
icon: new Icon(Icons.save),
|
||||
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:
|
||||
return <Widget>[];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import 'package:flutter_simple_dependency_injection/injector.dart';
|
|||
import 'package:redux/redux.dart';
|
||||
import 'package:sentry/sentry.dart';
|
||||
|
||||
import 'firebaseMessagingConf.dart';
|
||||
import 'firesApp.dart';
|
||||
import 'globals.dart' as globals;
|
||||
import 'models/appState.dart';
|
||||
|
|
@ -27,6 +26,7 @@ void mainCommon(List<Middleware<AppState>> otherMiddleware) {
|
|||
initialState: new AppState(
|
||||
gmapKey: secrets['gmapKey'],
|
||||
firesApiKey: secrets['firesApiKey'],
|
||||
serverUrl: secrets['firesApiUrl'],
|
||||
firesApiUrl: secrets['firesApiUrl'] + "api/v1/"),
|
||||
middleware: List.from(otherMiddleware)
|
||||
..add(fetchDataMiddleware));
|
||||
|
|
@ -37,8 +37,6 @@ void mainCommon(List<Middleware<AppState>> otherMiddleware) {
|
|||
|
||||
VoidCallback mainFn = () {
|
||||
loadYourLocations().then((yl) {
|
||||
firebaseConfig(store);
|
||||
|
||||
// Run baby run!
|
||||
runApp(new FiresApp(store));
|
||||
});
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ void main() {
|
|||
return ">>>>> ${action.toString().replaceAll('Instance of ', '')}";
|
||||
}
|
||||
|
||||
LogLevel logRedux = LogLevel.actions;
|
||||
LogLevel logRedux = LogLevel.full;
|
||||
|
||||
List<Middleware> devMiddlewares = logRedux == LogLevel.none
|
||||
? []
|
||||
|
|
|
|||
|
|
@ -1,16 +1,38 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'colors.dart';
|
||||
import 'sandbox.dart';
|
||||
import 'activeFires.dart';
|
||||
import 'globals.dart' as globals;
|
||||
import 'generated/i18n.dart';
|
||||
import 'package:badge/badge.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 'supportPage.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 {
|
||||
MainDrawer(BuildContext context, {key})
|
||||
|
|
@ -18,101 +40,115 @@ class MainDrawer extends Drawer {
|
|||
}
|
||||
|
||||
Widget mainDrawer(BuildContext context) {
|
||||
return new ListView(
|
||||
// Important: Remove any padding from the ListView.
|
||||
padding: EdgeInsets.zero,
|
||||
children: listWithoutNulls(<Widget>[
|
||||
new GestureDetector(
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
Navigator.pushNamed(context, '/');
|
||||
},
|
||||
child: new DrawerHeader(
|
||||
child: new Column(
|
||||
children: <Widget>[
|
||||
new Image.asset(
|
||||
'images/logo-200.png',
|
||||
fit: BoxFit.scaleDown,
|
||||
height: 80.0,
|
||||
return new StoreConnector<AppState, _ViewModel>(
|
||||
distinct: true,
|
||||
converter: (store) {
|
||||
return new _ViewModel(unreadCount: store.state.fireNotificationsUnread);
|
||||
},
|
||||
builder: (context, view) {
|
||||
return new ListView(
|
||||
// Important: Remove any padding from the ListView.
|
||||
padding: EdgeInsets.zero,
|
||||
children: listWithoutNulls(<Widget>[
|
||||
new GestureDetector(
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
Navigator.pushNamed(context, '/');
|
||||
},
|
||||
child: new DrawerHeader(
|
||||
child: new Column(
|
||||
children: <Widget>[
|
||||
new Image.asset(
|
||||
'images/logo-200.png',
|
||||
fit: BoxFit.scaleDown,
|
||||
height: 80.0,
|
||||
),
|
||||
const SizedBox(height: 20.0),
|
||||
new Text(S.of(context).appName,
|
||||
style: new TextStyle(
|
||||
fontSize: 24.0,
|
||||
color: Colors.white,
|
||||
)),
|
||||
],
|
||||
),
|
||||
decoration: new BoxDecoration(
|
||||
color: fires300,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20.0),
|
||||
new Text(S.of(context).appName,
|
||||
style: new TextStyle(
|
||||
fontSize: 24.0,
|
||||
color: Colors.white,
|
||||
)),
|
||||
],
|
||||
),
|
||||
decoration: new BoxDecoration(
|
||||
color: fires300,
|
||||
),
|
||||
),
|
||||
),
|
||||
new ListTile(
|
||||
leading: const Icon(Icons.whatshot),
|
||||
title: new Text(S.of(context).activeFires),
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
Navigator.pushNamed(context, ActiveFiresPage.routeName);
|
||||
},
|
||||
),
|
||||
new ListTile(
|
||||
leading: const Icon(Icons.notifications_active),
|
||||
title: new Text(S.of(context).notifyAFire),
|
||||
onTap: () {
|
||||
// Then close the drawer
|
||||
Navigator.pop(context);
|
||||
Navigator.pushNamed(context, FireAlert.routeName);
|
||||
},
|
||||
),
|
||||
new ListTile(
|
||||
leading: const Icon(Icons.notifications),
|
||||
title: new Text(S.of(context).fireNotificationsTitleShort),
|
||||
onTap: () {
|
||||
// Then close the drawer
|
||||
Navigator.pop(context);
|
||||
Navigator.pushNamed(context, FireNotificationList.routeName);
|
||||
},
|
||||
),
|
||||
new Divider(),
|
||||
new ListTile(
|
||||
leading: const Icon(Icons.favorite),
|
||||
title: new Text(S.of(context).supportThisInitiative),
|
||||
onTap: () {
|
||||
// Then close the drawer
|
||||
Navigator.pop(context);
|
||||
Navigator.pushNamed(context, SupportPage.routeName);
|
||||
},
|
||||
),
|
||||
new ListTile(
|
||||
leading: const Icon(Icons.lock),
|
||||
title: new Text(S.of(context).privacyPolicy),
|
||||
onTap: () {
|
||||
// Then close the drawer
|
||||
Navigator.pop(context);
|
||||
Navigator.pushNamed(context, PrivacyPage.routeName);
|
||||
},
|
||||
),
|
||||
globals.isDevelopment ?
|
||||
new ListTile(
|
||||
leading: const Icon(Icons.bug_report),
|
||||
title: new Text('Sandbox'),
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
Navigator.pushNamed(context, Sandbox.routeName);
|
||||
},
|
||||
): null,
|
||||
new AboutListTile(
|
||||
icon: globals.appIcon,
|
||||
applicationName: S.of(context).appName,
|
||||
applicationVersion: globals.appVersion,
|
||||
applicationIcon: globals.appMediumIcon,
|
||||
applicationLegalese: S.of(context).appLicense(DateTime.now().year.toString()),
|
||||
aboutBoxChildren: <Widget> [
|
||||
// new Text('What?')
|
||||
]
|
||||
// FIXME
|
||||
)
|
||||
])
|
||||
);
|
||||
new ListTile(
|
||||
leading: const Icon(Icons.whatshot),
|
||||
title: new Text(S.of(context).activeFires),
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
Navigator.pushNamed(context, ActiveFiresPage.routeName);
|
||||
},
|
||||
),
|
||||
new ListTile(
|
||||
leading: const Icon(Icons.notifications_active),
|
||||
title: new Text(S.of(context).notifyAFire),
|
||||
onTap: () {
|
||||
// Then close the drawer
|
||||
Navigator.pop(context);
|
||||
Navigator.pushNamed(context, FireAlert.routeName);
|
||||
},
|
||||
),
|
||||
new ListTile(
|
||||
leading: const Icon(Icons.notifications),
|
||||
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: () {
|
||||
// Then close the drawer
|
||||
Navigator.pop(context);
|
||||
Navigator.pushNamed(context, FireNotificationList.routeName);
|
||||
},
|
||||
),
|
||||
new Divider(),
|
||||
new ListTile(
|
||||
leading: const Icon(Icons.favorite),
|
||||
title: new Text(S.of(context).supportThisInitiative),
|
||||
onTap: () {
|
||||
// Then close the drawer
|
||||
Navigator.pop(context);
|
||||
Navigator.pushNamed(context, SupportPage.routeName);
|
||||
},
|
||||
),
|
||||
new ListTile(
|
||||
leading: const Icon(Icons.lock),
|
||||
title: new Text(S.of(context).privacyPolicy),
|
||||
onTap: () {
|
||||
// Then close the drawer
|
||||
Navigator.pop(context);
|
||||
Navigator.pushNamed(context, PrivacyPage.routeName);
|
||||
},
|
||||
),
|
||||
globals.isDevelopment
|
||||
? new ListTile(
|
||||
leading: const Icon(Icons.bug_report),
|
||||
title: new Text('Sandbox'),
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
Navigator.pushNamed(context, Sandbox.routeName);
|
||||
},
|
||||
)
|
||||
: null,
|
||||
new AboutListTile(
|
||||
icon: globals.appIcon,
|
||||
applicationName: S.of(context).appName,
|
||||
applicationVersion: globals.appVersion,
|
||||
applicationIcon: globals.appMediumIcon,
|
||||
applicationLegalese:
|
||||
S.of(context).appLicense(DateTime.now().year.toString()),
|
||||
aboutBoxChildren: <Widget>[
|
||||
// new Text('What?')
|
||||
]
|
||||
// FIXME
|
||||
)
|
||||
]));
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +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:fires_flutter/models/yourLocation.dart';
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
import 'package:meta/meta.dart';
|
||||
|
||||
|
|
@ -25,12 +25,16 @@ class AppState extends Object with _$AppStateSerializerMixin {
|
|||
@JsonKey(ignore: true)
|
||||
final String gmapKey;
|
||||
@JsonKey(ignore: true)
|
||||
final String serverUrl;
|
||||
@JsonKey(ignore: true)
|
||||
final String firesApiKey;
|
||||
@JsonKey(ignore: true)
|
||||
final String firesApiUrl;
|
||||
final List<YourLocation> yourLocations;
|
||||
final List<FireNotification> fireNotifications;
|
||||
@JsonKey(ignore: true)
|
||||
final int fireNotificationsUnread;
|
||||
@JsonKey(ignore: true)
|
||||
final FireMapState fireMapState;
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
|
|
@ -40,13 +44,15 @@ class AppState extends Object with _$AppStateSerializerMixin {
|
|||
AppState(
|
||||
{this.yourLocations: const <YourLocation>[],
|
||||
this.fireNotifications: const <FireNotification>[],
|
||||
this.fireNotificationsUnread: 0,
|
||||
this.user: const User.initial(),
|
||||
this.isLoading: false,
|
||||
this.isLoaded: false,
|
||||
this.error: null,
|
||||
this.gmapKey,
|
||||
this.firesApiKey,
|
||||
this.firesApiUrl,
|
||||
this.firesApiUrl,
|
||||
this.serverUrl,
|
||||
this.fireMapState: const FireMapState.initial()});
|
||||
|
||||
AppState copyWith(
|
||||
|
|
@ -55,10 +61,12 @@ class AppState extends Object with _$AppStateSerializerMixin {
|
|||
String user,
|
||||
String error,
|
||||
String gmapKey,
|
||||
String firesApiKey,
|
||||
String firesApiKey,
|
||||
String serverUrl,
|
||||
String firesApiUrl,
|
||||
List<YourLocation> yourLocations,
|
||||
List<FireNotification> fireNotifications,
|
||||
List<FireNotification> fireNotifications,
|
||||
int fireNotificationsUnread,
|
||||
FireMapState fireMapState}) {
|
||||
return new AppState(
|
||||
isLoading: isLoading ?? this.isLoading,
|
||||
|
|
@ -67,9 +75,12 @@ class AppState extends Object with _$AppStateSerializerMixin {
|
|||
error: error ?? this.error,
|
||||
gmapKey: gmapKey ?? this.gmapKey,
|
||||
firesApiKey: firesApiKey ?? this.firesApiKey,
|
||||
firesApiUrl: firesApiUrl ?? this.firesApiUrl,
|
||||
firesApiUrl: firesApiUrl ?? this.firesApiUrl,
|
||||
serverUrl: serverUrl ?? this.serverUrl,
|
||||
yourLocations: yourLocations ?? this.yourLocations,
|
||||
fireNotifications: fireNotifications ?? this.fireNotifications,
|
||||
fireNotificationsUnread:
|
||||
fireNotificationsUnread ?? this.fireNotificationsUnread,
|
||||
fireMapState: fireMapState ?? this.fireMapState);
|
||||
}
|
||||
|
||||
|
|
@ -77,8 +88,8 @@ class AppState extends Object with _$AppStateSerializerMixin {
|
|||
String toString() {
|
||||
return 'AppState{\nuser: ${user}\nisLoading: $isLoading\nisLoaded: $isLoaded\napiKey: ${ellipse(
|
||||
firesApiKey, 8)}\napiUrl: ${ellipse(
|
||||
firesApiUrl, 8)}\nyourLocations count: ${yourLocations
|
||||
.length}\nyourLocations: ${yourLocations}\nfireNotifications: ${fireNotifications}\nfireMapState: $fireMapState}';
|
||||
firesApiUrl, 8)}\nserverUrl: ${serverUrl}\nfireNotifications: ${fireNotifications}\nyourLocations count: ${yourLocations
|
||||
.length}\nunread notif: ${fireNotificationsUnread}\nyourLocations: ${yourLocations}\nfireMapState: $fireMapState}';
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,40 +1,53 @@
|
|||
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:redux/src/store.dart';
|
||||
|
||||
import '../genericMap.dart';
|
||||
import '../objectIdUtils.dart';
|
||||
import '../redux/actions.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)
|
||||
@JsonKey(toJson: objectIdToJson, fromJson: objectIdFromJson)
|
||||
ObjectId id;
|
||||
final double lat;
|
||||
final double lon;
|
||||
final String description;
|
||||
final DateTime when;
|
||||
final String sealed;
|
||||
@JsonKey(toJson: objectIdToJson, fromJson: objectIdFromJson)
|
||||
final ObjectId subsId;
|
||||
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();
|
||||
}
|
||||
|
||||
FireNotification copyWith({id, lat, lon, description, read, when}) {
|
||||
FireNotification copyWith(
|
||||
{id, lat, lon, description, read, when, sealed, subsId}) {
|
||||
return new FireNotification(
|
||||
id: id ?? this.id,
|
||||
lat: lat ?? this.lat,
|
||||
lon: lon ?? this.lon,
|
||||
read: read ?? this.read,
|
||||
lon: lon ?? this.lon,
|
||||
read: read ?? this.read,
|
||||
description: description ?? this.description,
|
||||
sealed: sealed ?? this.sealed,
|
||||
subsId: subsId ?? this.subsId,
|
||||
when: when ?? this.when);
|
||||
}
|
||||
|
||||
|
|
@ -45,16 +58,46 @@ class FireNotification extends Object with _$FireNotificationSerializerMixin {
|
|||
runtimeType == other.runtimeType &&
|
||||
id == other.id &&
|
||||
lat == other.lat &&
|
||||
lon == other.lon &&
|
||||
read == other.read &&
|
||||
lon == other.lon &&
|
||||
read == other.read &&
|
||||
description == other.description &&
|
||||
sealed == other.sealed &&
|
||||
subsId == other.subsId &&
|
||||
when == other.when;
|
||||
|
||||
@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
|
||||
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()));
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,12 +10,14 @@ part of 'fireNotification.dart';
|
|||
|
||||
FireNotification _$FireNotificationFromJson(Map<String, dynamic> json) =>
|
||||
new FireNotification(
|
||||
id: _objectIdFromJson(json['id'] as String),
|
||||
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);
|
||||
read: json['read'] as bool,
|
||||
sealed: json['sealed'] as String,
|
||||
subsId: objectIdFromJson(json['subsId'] as String));
|
||||
|
||||
abstract class _$FireNotificationSerializerMixin {
|
||||
ObjectId get id;
|
||||
|
|
@ -23,6 +25,8 @@ abstract class _$FireNotificationSerializerMixin {
|
|||
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);
|
||||
}
|
||||
|
|
@ -32,15 +36,23 @@ class _$FireNotificationJsonMapWrapper extends $JsonMapWrapper {
|
|||
_$FireNotificationJsonMapWrapper(this._v);
|
||||
|
||||
@override
|
||||
Iterable<String> get keys =>
|
||||
const ['id', 'lat', 'lon', 'description', 'when', 'read'];
|
||||
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);
|
||||
return objectIdToJson(_v.id);
|
||||
case 'lat':
|
||||
return _v.lat;
|
||||
case 'lon':
|
||||
|
|
@ -49,6 +61,10 @@ class _$FireNotificationJsonMapWrapper extends $JsonMapWrapper {
|
|||
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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,20 +1,15 @@
|
|||
import 'package:bson_objectid/bson_objectid.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
import 'package:fires_flutter/objectIdUtils.dart';
|
||||
|
||||
part 'yourLocation.g.dart';
|
||||
|
||||
_objectIdFromJson(String json) {
|
||||
return new ObjectId.fromHexString(json);
|
||||
}
|
||||
|
||||
_objectIdToJson(ObjectId o) {
|
||||
return o.toString();
|
||||
}
|
||||
|
||||
@JsonSerializable(nullable: false)
|
||||
class YourLocation extends Object with _$YourLocationSerializerMixin {
|
||||
@JsonKey(toJson: _objectIdToJson, fromJson: _objectIdFromJson)
|
||||
@JsonKey(toJson: objectIdToJson, fromJson: objectIdFromJson)
|
||||
ObjectId id;
|
||||
final double lat;
|
||||
final double lon;
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ part of 'yourLocation.dart';
|
|||
|
||||
YourLocation _$YourLocationFromJson(Map<String, dynamic> json) =>
|
||||
new YourLocation(
|
||||
id: _objectIdFromJson(json['id'] as String),
|
||||
id: objectIdFromJson(json['id'] as String),
|
||||
lat: (json['lat'] as num).toDouble(),
|
||||
lon: (json['lon'] as num).toDouble(),
|
||||
description: json['description'] as String,
|
||||
|
|
@ -40,7 +40,7 @@ class _$YourLocationJsonMapWrapper extends $JsonMapWrapper {
|
|||
if (key is String) {
|
||||
switch (key) {
|
||||
case 'id':
|
||||
return _objectIdToJson(_v.id);
|
||||
return objectIdToJson(_v.id);
|
||||
case 'lat':
|
||||
return _v.lat;
|
||||
case 'lon':
|
||||
|
|
|
|||
9
lib/objectIdUtils.dart
Normal file
9
lib/objectIdUtils.dart
Normal 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();
|
||||
}
|
||||
|
|
@ -41,6 +41,7 @@ class OnUserLangAction extends AppActions {
|
|||
|
||||
class FetchFireNotificationsSucceededAction extends AppActions {
|
||||
final List<FireNotification> fetchedFireNotifications;
|
||||
final int unreadCount;
|
||||
|
||||
FetchFireNotificationsSucceededAction(this.fetchedFireNotifications);
|
||||
FetchFireNotificationsSucceededAction(this.fetchedFireNotifications, this.unreadCount);
|
||||
}
|
||||
|
|
@ -6,7 +6,16 @@ AppState appReducer(AppState state, action) {
|
|||
return state.copyWith(yourLocations: action.fetchedYourLocations);
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import 'package:bson_objectid/bson_objectid.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:just_debounce_it/just_debounce_it.dart';
|
||||
import 'package:redux/redux.dart';
|
||||
|
|
@ -161,8 +162,12 @@ void fetchDataMiddleware(Store<AppState> store, action, NextDispatcher next) {
|
|||
|
||||
if (action is FetchFireNotificationsAction) {
|
||||
loadFireNotifications().then((fireNotifications) {
|
||||
int unread = 0;
|
||||
for (FireNotification notif in fireNotifications) {
|
||||
if (!notif.read) { unread++; }
|
||||
}
|
||||
store.dispatch(
|
||||
new FetchFireNotificationsSucceededAction(fireNotifications));
|
||||
new FetchFireNotificationsSucceededAction(fireNotifications, unread));
|
||||
persistFireNotifications(fireNotifications);
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ FireMapState _showYourLocationMap(
|
|||
status: action.loc.subscribed
|
||||
? FireMapStatus.unsubscribe
|
||||
: FireMapStatus.view,
|
||||
yourLocation: action.loc);
|
||||
yourLocation: action.loc, fireNotication: null);
|
||||
}
|
||||
|
||||
FireMapState _showFireNotificationMap(
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ final fireNotificationReducer = combineReducers<List<FireNotification>>([
|
|||
|
||||
List<FireNotification> _receivedFireNotification(
|
||||
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(
|
||||
|
|
|
|||
|
|
@ -1,29 +1,28 @@
|
|||
import '../models/appState.dart';
|
||||
import 'appReducer.dart';
|
||||
import 'errorReducer.dart';
|
||||
import 'fireMapReducer.dart';
|
||||
import 'fireNotificationReducer.dart';
|
||||
import 'loadedReducer.dart';
|
||||
import 'loadingReducer.dart';
|
||||
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) {
|
||||
var state = prevState;
|
||||
if (action is AppActions)
|
||||
state = appReducer(prevState, action);
|
||||
var state = appReducer(prevState, action);
|
||||
return AppState(
|
||||
yourLocations: yourLocationsReducer(state.yourLocations, action),
|
||||
fireNotifications: fireNotificationReducer(state.fireNotifications, action),
|
||||
yourLocations: yourLocationsReducer(state.yourLocations, action),
|
||||
fireNotifications:
|
||||
fireNotificationReducer(state.fireNotifications, action),
|
||||
fireNotificationsUnread: state.fireNotificationsUnread,
|
||||
user: userReducer(state.user, action),
|
||||
isLoading: loadingReducer(state.isLoading, action),
|
||||
isLoaded: loadedReducer(state.isLoaded, action),
|
||||
error: errorReducer(state.error, action),
|
||||
fireMapState: fireMapReducer(state.fireMapState, action),
|
||||
firesApiKey: state.firesApiKey,
|
||||
firesApiUrl: state.firesApiUrl,
|
||||
gmapKey: state.gmapKey
|
||||
);
|
||||
firesApiKey: state.firesApiKey,
|
||||
firesApiUrl: state.firesApiUrl,
|
||||
serverUrl: state.serverUrl,
|
||||
gmapKey: state.gmapKey);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,6 +50,8 @@ dependencies:
|
|||
# https://pub.dartlang.org/packages/padder
|
||||
padder: "^1.0.1"
|
||||
flutter_fab_dialer: "^0.0.5"
|
||||
badge: "^0.0.2"
|
||||
flutter_spinkit: "^1.0.0"
|
||||
|
||||
# firebase
|
||||
firebase_messaging: "^1.0.4"
|
||||
|
|
|
|||
|
|
@ -59,5 +59,7 @@
|
|||
"fireNotificationsDescription": "Here you will receive fire notifications in locations you are subscribed to",
|
||||
"areYouSureTitle": "Are you sure?",
|
||||
"deleteAllFireNotificationsAlertDescription": "This will remove all your fire notifications",
|
||||
"DELETE": "DELETE"
|
||||
"DELETE": "DELETE",
|
||||
"SHOW": "SHOW",
|
||||
"CLOSE": "CLOSE"
|
||||
}
|
||||
|
|
@ -59,5 +59,7 @@
|
|||
"fireNotificationsDescription": "Aquí recibirás las notificaciones de fuegos en los lugares a los que te subscribas",
|
||||
"areYouSureTitle": "¿Seguro?",
|
||||
"deleteAllFireNotificationsAlertDescription": "Esto borrará todas las notificaciones de fuegos",
|
||||
"DELETE": "BORRAR"
|
||||
"DELETE": "BORRAR",
|
||||
"SHOW": "MOSTRAR",
|
||||
"CLOSE": "CERRAR"
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue