More redux work
This commit is contained in:
parent
7dddf03e32
commit
657fecdf17
9 changed files with 278 additions and 219 deletions
|
|
@ -13,19 +13,23 @@ import 'globalFiresBottomStats.dart';
|
||||||
import 'locationUtils.dart';
|
import 'locationUtils.dart';
|
||||||
import 'mainDrawer.dart';
|
import 'mainDrawer.dart';
|
||||||
import 'models/appState.dart';
|
import 'models/appState.dart';
|
||||||
|
import 'package:redux/src/store.dart';
|
||||||
import 'placesAutocompleteUtils.dart';
|
import 'placesAutocompleteUtils.dart';
|
||||||
import 'redux/actions.dart';
|
import 'redux/actions.dart';
|
||||||
|
|
||||||
|
@immutable
|
||||||
class _ViewModel {
|
class _ViewModel {
|
||||||
final List<YourLocation> yourLocations;
|
final List<YourLocation> yourLocations;
|
||||||
final AddYourLocationFunction onAdd;
|
final AddYourLocationFunction onAdd;
|
||||||
final DeleteYourLocationFunction onDelete;
|
final DeleteYourLocationFunction onDelete;
|
||||||
final ToggleSubscriptionFunction onToggleSubs;
|
final ToggleSubscriptionFunction onToggleSubs;
|
||||||
|
final OnLocationTapFunction onTap;
|
||||||
|
|
||||||
_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.yourLocations});
|
@required this.yourLocations});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
@ -36,14 +40,16 @@ class _ViewModel {
|
||||||
yourLocations == other.yourLocations &&
|
yourLocations == other.yourLocations &&
|
||||||
onAdd == other.onAdd &&
|
onAdd == other.onAdd &&
|
||||||
onDelete == other.onDelete &&
|
onDelete == other.onDelete &&
|
||||||
onToggleSubs == other.onToggleSubs;
|
onToggleSubs == other.onToggleSubs &&
|
||||||
|
onTap == other.onTap;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode =>
|
int get hashCode =>
|
||||||
yourLocations.hashCode ^
|
yourLocations.hashCode ^
|
||||||
onAdd.hashCode ^
|
onAdd.hashCode ^
|
||||||
onDelete.hashCode ^
|
onDelete.hashCode ^
|
||||||
onToggleSubs.hashCode;
|
onToggleSubs.hashCode ^
|
||||||
|
onTap.hashCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
class ActiveFiresPage extends StatefulWidget {
|
class ActiveFiresPage extends StatefulWidget {
|
||||||
|
|
@ -81,7 +87,7 @@ class _ActiveFiresPageState extends State<ActiveFiresPage> {
|
||||||
|
|
||||||
_ActiveFiresPageState();
|
_ActiveFiresPageState();
|
||||||
|
|
||||||
Widget _buildRow(YourLocation loc, onToggle) {
|
Widget _buildRow(YourLocation loc, onToggle, onTap) {
|
||||||
return new ListTile(
|
return new ListTile(
|
||||||
dense: true,
|
dense: true,
|
||||||
leading: const Icon(Icons.location_on),
|
leading: const Icon(Icons.location_on),
|
||||||
|
|
@ -92,44 +98,29 @@ class _ActiveFiresPageState extends State<ActiveFiresPage> {
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
loc.subscribed = !loc.subscribed;
|
loc.subscribed = !loc.subscribed;
|
||||||
onToggle(loc);
|
onToggle(loc);
|
||||||
/* FIXME int i = globals.yourLocations.indexOf(loc);
|
|
||||||
globals.yourLocations.removeAt(i);
|
|
||||||
globals.yourLocations.insert(i, loc);
|
|
||||||
persistYourLocations(); */
|
|
||||||
setState(() {});
|
|
||||||
}),
|
}),
|
||||||
title: new Text(loc.description),
|
title: new Text(loc.description),
|
||||||
onLongPress: () {
|
onLongPress: () {
|
||||||
showSnackMsg(S.of(context).toDeleteThisPlace);
|
showSnackMsg(S.of(context).toDeleteThisPlace);
|
||||||
},
|
},
|
||||||
onTap: () {
|
onTap: () {
|
||||||
showLocationMap(loc);
|
onTap(loc);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void showLocationMap(YourLocation loc) {
|
|
||||||
// , VoidCallback onSubs
|
|
||||||
Navigator.push(
|
|
||||||
context,
|
|
||||||
new MaterialPageRoute(
|
|
||||||
builder: (context) => new GenericMap(
|
|
||||||
title: loc.description,
|
|
||||||
location: loc,
|
|
||||||
operation: MapOperation.view)));
|
|
||||||
}
|
|
||||||
|
|
||||||
void showSnackMsg(String msg) {
|
void showSnackMsg(String msg) {
|
||||||
_scaffoldKey.currentState.showSnackBar(new SnackBar(
|
_scaffoldKey.currentState.showSnackBar(new SnackBar(
|
||||||
content: new Text(msg),
|
content: new Text(msg),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildSavedLocations(List<YourLocation> yl, onDeleted, onToggle) {
|
Widget _buildSavedLocations(
|
||||||
|
List<YourLocation> yl, onDeleted, onToggle, onTap) {
|
||||||
return new ListView.builder(
|
return new ListView.builder(
|
||||||
padding: const EdgeInsets.all(16.0),
|
padding: const EdgeInsets.all(16.0),
|
||||||
itemCount: yl.length,
|
itemCount: yl.length,
|
||||||
itemBuilder: (BuildContext _context, int i) {
|
itemBuilder: (BuildContext _context, int i) {
|
||||||
return _buildItem(yl.elementAt(i), onDeleted, onToggle);
|
return _buildItem(yl.elementAt(i), onDeleted, onToggle, onTap);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -141,7 +132,7 @@ class _ActiveFiresPageState extends State<ActiveFiresPage> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildItem(YourLocation item, onDelete, onToggle) {
|
Widget _buildItem(YourLocation item, onDelete, onToggle, onTap) {
|
||||||
final ThemeData theme = Theme.of(context);
|
final ThemeData theme = Theme.of(context);
|
||||||
return new Dismissible(
|
return new Dismissible(
|
||||||
key: new ObjectKey(item),
|
key: new ObjectKey(item),
|
||||||
|
|
@ -174,7 +165,7 @@ class _ActiveFiresPageState extends State<ActiveFiresPage> {
|
||||||
color: theme.canvasColor,
|
color: theme.canvasColor,
|
||||||
border: new Border(
|
border: new Border(
|
||||||
bottom: new BorderSide(color: theme.dividerColor))),
|
bottom: new BorderSide(color: theme.dividerColor))),
|
||||||
child: _buildRow(item, onToggle)));
|
child: _buildRow(item, onToggle, onTap)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
@ -185,6 +176,7 @@ class _ActiveFiresPageState extends State<ActiveFiresPage> {
|
||||||
return new _ViewModel(
|
return new _ViewModel(
|
||||||
onAdd: (loc) {
|
onAdd: (loc) {
|
||||||
store.dispatch(new AddYourLocationAction(loc));
|
store.dispatch(new AddYourLocationAction(loc));
|
||||||
|
gotoLocationMap(store, loc, context);
|
||||||
},
|
},
|
||||||
onDelete: (id) {
|
onDelete: (id) {
|
||||||
store.dispatch(new DeleteYourLocationAction(id));
|
store.dispatch(new DeleteYourLocationAction(id));
|
||||||
|
|
@ -192,6 +184,9 @@ class _ActiveFiresPageState extends State<ActiveFiresPage> {
|
||||||
onToggleSubs: (loc) {
|
onToggleSubs: (loc) {
|
||||||
store.dispatch(new ToggleSubscriptionAction(loc));
|
store.dispatch(new ToggleSubscriptionAction(loc));
|
||||||
},
|
},
|
||||||
|
onTap: (loc) {
|
||||||
|
gotoLocationMap(store, loc, context);
|
||||||
|
},
|
||||||
yourLocations: store.state.yourLocations);
|
yourLocations: store.state.yourLocations);
|
||||||
},
|
},
|
||||||
builder: (context, view) {
|
builder: (context, view) {
|
||||||
|
|
@ -219,8 +214,8 @@ class _ActiveFiresPageState extends State<ActiveFiresPage> {
|
||||||
bottomNavigationBar: new GlobalFiresBottomStats(),
|
bottomNavigationBar: new GlobalFiresBottomStats(),
|
||||||
body: hasLocations
|
body: hasLocations
|
||||||
? new Stack(children: <Widget>[
|
? new Stack(children: <Widget>[
|
||||||
_buildSavedLocations(
|
_buildSavedLocations(view.yourLocations, view.onDelete,
|
||||||
view.yourLocations, view.onDelete, view.onToggleSubs),
|
view.onToggleSubs, view.onTap),
|
||||||
new FabDialer(_fabMiniMenuItemList(view.onAdd), fires600,
|
new FabDialer(_fabMiniMenuItemList(view.onAdd), fires600,
|
||||||
new Icon(Icons.add))
|
new Icon(Icons.add))
|
||||||
])
|
])
|
||||||
|
|
@ -242,6 +237,12 @@ 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()));
|
||||||
|
}
|
||||||
|
|
||||||
void onAddYourLocation(AddYourLocationFunction onAdd) {
|
void onAddYourLocation(AddYourLocationFunction onAdd) {
|
||||||
Future<YourLocation> location = getUserLocation(_scaffoldKey);
|
Future<YourLocation> location = getUserLocation(_scaffoldKey);
|
||||||
_saveLocation(location, onAdd);
|
_saveLocation(location, onAdd);
|
||||||
|
|
@ -256,17 +257,6 @@ class _ActiveFiresPageState extends State<ActiveFiresPage> {
|
||||||
location.then((newLocation) {
|
location.then((newLocation) {
|
||||||
if (newLocation != YourLocation.noLocation) {
|
if (newLocation != YourLocation.noLocation) {
|
||||||
onAdd(newLocation);
|
onAdd(newLocation);
|
||||||
/* FIXME
|
|
||||||
if (globals.yourLocations.contains(newLocation)) {
|
|
||||||
showSnackMsg(S.of(context).addedThisLocation);
|
|
||||||
} else
|
|
||||||
this.setState(() {
|
|
||||||
globals.yourLocations.add(newLocation);
|
|
||||||
persistYourLocations();
|
|
||||||
new Timer(new Duration(milliseconds: 1000), () {
|
|
||||||
showLocationMap(newLocation);
|
|
||||||
});
|
|
||||||
}); */
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,14 +3,16 @@ 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:firebase_messaging/firebase_messaging.dart';
|
||||||
|
import 'package:fires_flutter/models/basicLocation.dart';
|
||||||
|
import 'package:fires_flutter/models/yourLocation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_map/flutter_map.dart';
|
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:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
import 'package:just_debounce_it/just_debounce_it.dart';
|
import 'package:just_debounce_it/just_debounce_it.dart';
|
||||||
import 'package:latlong/latlong.dart';
|
import 'package:latlong/latlong.dart';
|
||||||
|
|
||||||
import 'package:fires_flutter/models/basicLocation.dart';
|
|
||||||
import 'colors.dart';
|
import 'colors.dart';
|
||||||
import 'customBottomAppBar.dart';
|
import 'customBottomAppBar.dart';
|
||||||
import 'dummyMapPlugin.dart';
|
import 'dummyMapPlugin.dart';
|
||||||
|
|
@ -18,47 +20,61 @@ import 'fireMarkType.dart';
|
||||||
import 'fireMarker.dart';
|
import 'fireMarker.dart';
|
||||||
import 'generated/i18n.dart';
|
import 'generated/i18n.dart';
|
||||||
import 'globals.dart' as globals;
|
import 'globals.dart' as globals;
|
||||||
|
import 'models/appState.dart';
|
||||||
|
import 'models/fireMapState.dart';
|
||||||
|
import 'redux/actions.dart';
|
||||||
import 'slider.dart';
|
import 'slider.dart';
|
||||||
import 'package:fires_flutter/models/yourLocation.dart';
|
|
||||||
import 'zoomMapPlugin.dart';
|
import 'zoomMapPlugin.dart';
|
||||||
|
|
||||||
enum MapOperation { view, subscriptionConfirm, unsubscribe }
|
@immutable
|
||||||
|
class _ViewModel {
|
||||||
|
final FireMapState mapState;
|
||||||
|
final OnSubscribeFunction onSubs;
|
||||||
|
final OnSubscribeConfirmedFunction onSubsConfirmed;
|
||||||
|
final OnUnSubscribeFunction onUnSubs;
|
||||||
|
|
||||||
class GenericMap extends StatefulWidget {
|
_ViewModel(
|
||||||
final YourLocation location;
|
{@required this.mapState,
|
||||||
final String title;
|
@required this.onSubs,
|
||||||
final MapOperation operation;
|
@required this.onSubsConfirmed,
|
||||||
|
@required this.onUnSubs});
|
||||||
GenericMap(
|
|
||||||
{@required this.title,
|
|
||||||
@required this.location,
|
|
||||||
@required this.operation});
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_GenericMapState createState() =>
|
bool operator ==(Object other) =>
|
||||||
_GenericMapState(title: title, location: location, operation: operation);
|
identical(this, other) ||
|
||||||
|
other is _ViewModel &&
|
||||||
|
runtimeType == other.runtimeType &&
|
||||||
|
mapState == other.mapState &&
|
||||||
|
onSubs == other.onSubs &&
|
||||||
|
onSubsConfirmed == other.onSubsConfirmed &&
|
||||||
|
onUnSubs == other.onUnSubs;
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode =>
|
||||||
|
mapState.hashCode ^
|
||||||
|
onSubs.hashCode ^
|
||||||
|
onSubsConfirmed.hashCode ^
|
||||||
|
onUnSubs.hashCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
class GenericMap extends StatefulWidget {
|
||||||
|
GenericMap();
|
||||||
|
|
||||||
|
@override
|
||||||
|
_GenericMapState createState() => _GenericMapState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _GenericMapState extends State<GenericMap> {
|
class _GenericMapState extends State<GenericMap> {
|
||||||
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
|
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
|
||||||
final FirebaseMessaging _firebaseMessaging = new FirebaseMessaging();
|
final FirebaseMessaging _firebaseMessaging = new FirebaseMessaging();
|
||||||
|
|
||||||
final YourLocation location;
|
|
||||||
final String title;
|
|
||||||
int numFires;
|
int numFires;
|
||||||
int kmAround = 100;
|
int kmAround = 100;
|
||||||
List<dynamic> fires = [];
|
List<dynamic> fires = [];
|
||||||
List<dynamic> falsePos = [];
|
List<dynamic> falsePos = [];
|
||||||
List<dynamic> industries = [];
|
List<dynamic> industries = [];
|
||||||
MapOperation operation;
|
|
||||||
|
|
||||||
@override
|
void updateFireStats(YourLocation location) {
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
updateFireStats();
|
|
||||||
}
|
|
||||||
|
|
||||||
void updateFireStats() {
|
|
||||||
var url = '${globals.firesApiUrl}fires-in-full/${globals
|
var url = '${globals.firesApiUrl}fires-in-full/${globals
|
||||||
.firesApiKey}/${location.lat}/${location.lon}/$kmAround';
|
.firesApiKey}/${location.lat}/${location.lon}/$kmAround';
|
||||||
http.read(url).then((result) {
|
http.read(url).then((result) {
|
||||||
|
|
@ -81,145 +97,170 @@ class _GenericMapState extends State<GenericMap> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_GenericMapState(
|
_GenericMapState();
|
||||||
{@required this.title,
|
|
||||||
@required this.location,
|
|
||||||
@required this.operation});
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
print('Build map with operation: $operation');
|
// print('Build map with operation: $operation');
|
||||||
MapOptions mapOptions = new MapOptions(
|
return new StoreConnector<AppState, _ViewModel>(
|
||||||
center: new LatLng(this.location.lat, this.location.lon),
|
distinct: false, // FIXME
|
||||||
plugins: globals.isDevelopment
|
converter: (store) {
|
||||||
? [new ZoomMapPlugin()]
|
return new _ViewModel(
|
||||||
: [new DummyMapPlugin()],
|
onSubs: (loc) {
|
||||||
// this works ?
|
store.dispatch(new SubscribeAction(loc.id));
|
||||||
// interactive: false,
|
|
||||||
zoom: 13.0,
|
|
||||||
// THIS does not works as expected
|
|
||||||
// maxZoom: 6.0,
|
|
||||||
onPositionChanged: (positionCallback) {
|
|
||||||
// decouple
|
|
||||||
// print('${positionCallback.center}, ${positionCallback.zoom}');
|
|
||||||
});
|
|
||||||
var mapController = new MapController();
|
|
||||||
// mapController.fitBounds(bounds);
|
|
||||||
// mapController.center
|
|
||||||
var fmap = new Opacity(
|
|
||||||
opacity: operation == MapOperation.subscriptionConfirm ? 0.5 : 1.0,
|
|
||||||
child: new FlutterMap(
|
|
||||||
options: mapOptions,
|
|
||||||
mapController: mapController,
|
|
||||||
layers: [
|
|
||||||
new TileLayerOptions(
|
|
||||||
maxZoom: 6.0,
|
|
||||||
subdomains: ['a', 'b', 'c'],
|
|
||||||
urlTemplate:
|
|
||||||
'https://tiles.wmflabs.org/bw-mapnik/{z}/{x}/{y}.png',
|
|
||||||
additionalOptions: {
|
|
||||||
// 'opacity': '0.1',
|
|
||||||
'attribution':
|
|
||||||
'© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
|
|
||||||
},
|
},
|
||||||
),
|
onSubsConfirmed: (loc) {
|
||||||
globals.isDevelopment
|
store.dispatch(new SubscribeAction(loc.id));
|
||||||
? new ZoomMapPluginOptions()
|
},
|
||||||
: new DummyMapPluginOptions(),
|
onUnSubs: (loc) {
|
||||||
new MarkerLayerOptions(
|
store.dispatch(new UnSubscribeAction(loc.id));
|
||||||
markers: buildMarkers(
|
},
|
||||||
this.location, this.fires, this.industries, this.falsePos),
|
mapState: store.state.fireMapState);
|
||||||
),
|
},
|
||||||
],
|
builder: (context, view) {
|
||||||
));
|
YourLocation location = view.mapState.yourLocation;
|
||||||
final btnText = operation == MapOperation.view
|
MapOptions mapOptions = new MapOptions(
|
||||||
? S.of(context).toFiresNotifications
|
center: new LatLng(location.lat, location.lon),
|
||||||
: operation == MapOperation.subscriptionConfirm
|
plugins: globals.isDevelopment
|
||||||
? S.of(context).confirm
|
? [new ZoomMapPlugin()]
|
||||||
: S.of(context).unsubscribe;
|
: [new DummyMapPlugin()],
|
||||||
final btnIcon = operation == MapOperation.view
|
// this works ?
|
||||||
? Icons.notifications_active
|
// interactive: false,
|
||||||
: operation == MapOperation.subscriptionConfirm
|
zoom: 13.0,
|
||||||
? Icons.check
|
// THIS does not works as expected
|
||||||
: Icons.notifications_off;
|
// maxZoom: 6.0,
|
||||||
return new Scaffold(
|
onPositionChanged: (positionCallback) {
|
||||||
key: _scaffoldKey,
|
// decouple
|
||||||
appBar: new AppBar(
|
// print('${positionCallback.center}, ${positionCallback.zoom}');
|
||||||
title: new Text(title),
|
});
|
||||||
),
|
var mapController = new MapController();
|
||||||
floatingActionButton: FloatingActionButton.extended(
|
// mapController.fitBounds(bounds);
|
||||||
onPressed: () {
|
// mapController.center
|
||||||
setState(() {
|
|
||||||
switch (operation) {
|
FireMapStatus operation = view.mapState.status;
|
||||||
case MapOperation.view:
|
final btnText = operation == FireMapStatus.view
|
||||||
operation = MapOperation.subscriptionConfirm;
|
? S.of(context).toFiresNotifications
|
||||||
break;
|
: operation == FireMapStatus.subscriptionConfirm
|
||||||
case MapOperation.subscriptionConfirm:
|
? S.of(context).confirm
|
||||||
// IOS specific
|
: S.of(context).unsubscribe;
|
||||||
_firebaseMessaging.requestNotificationPermissions();
|
final btnIcon = operation == FireMapStatus.view
|
||||||
operation = MapOperation.unsubscribe;
|
? Icons.notifications_active
|
||||||
break;
|
: operation == FireMapStatus.subscriptionConfirm
|
||||||
case MapOperation.unsubscribe:
|
? Icons.check
|
||||||
operation = MapOperation.view;
|
: Icons.notifications_off;
|
||||||
break;
|
updateFireStats(location);
|
||||||
}
|
return new Scaffold(
|
||||||
});
|
key: _scaffoldKey,
|
||||||
},
|
appBar: new AppBar(
|
||||||
// https://github.com/flutter/flutter/issues/17583
|
title: new Text(location.description),
|
||||||
heroTag: "firesmap" + location.id.toHexString(),
|
),
|
||||||
icon: new Icon(btnIcon, color: fires600),
|
floatingActionButton: FloatingActionButton.extended(
|
||||||
label: new Text(
|
onPressed: () {
|
||||||
btnText,
|
|
||||||
style: const TextStyle(color: fires600),
|
switch (operation) {
|
||||||
),
|
case FireMapStatus.view:
|
||||||
backgroundColor: Colors.white,
|
view.onSubs(location);
|
||||||
),
|
break;
|
||||||
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
|
case FireMapStatus.subscriptionConfirm:
|
||||||
bottomNavigationBar: new CustomBottomAppBar(
|
view.onSubsConfirmed(location);
|
||||||
fabLocation: FloatingActionButtonLocation.centerFloat,
|
// IOS specific
|
||||||
showNotch: false,
|
_firebaseMessaging.requestNotificationPermissions();
|
||||||
color: fires100,
|
operation = FireMapStatus.unsubscribe;
|
||||||
// height: 170.0,
|
break;
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
case FireMapStatus.unsubscribe:
|
||||||
actions: listWithoutNulls(<Widget>[
|
view.onUnSubs(location);
|
||||||
operation == MapOperation.subscriptionConfirm || numFires == null
|
operation = FireMapStatus.view;
|
||||||
? null
|
break;
|
||||||
: new Text(numFires > 0
|
}
|
||||||
? S.of(context).firesAroundThisArea(
|
|
||||||
numFires.toString(), kmAround.toString())
|
},
|
||||||
: S
|
// https://github.com/flutter/flutter/issues/17583
|
||||||
.of(context)
|
heroTag: "firesmap" + location.id.toHexString(),
|
||||||
.noFiresAroundThisArea(kmAround.toString())),
|
icon: new Icon(btnIcon, color: fires600),
|
||||||
SizedBox(width: 10.0)
|
label: new Text(
|
||||||
])),
|
btnText,
|
||||||
body: LayoutBuilder(
|
style: const TextStyle(color: fires600),
|
||||||
builder: (context, constraints) =>
|
),
|
||||||
Stack(fit: StackFit.expand, children: <Widget>[
|
backgroundColor: Colors.white,
|
||||||
// Material(color: Colors.yellowAccent),
|
),
|
||||||
fmap,
|
floatingActionButtonLocation:
|
||||||
Positioned(
|
FloatingActionButtonLocation.centerFloat,
|
||||||
top: constraints.maxHeight - 200,
|
bottomNavigationBar: new CustomBottomAppBar(
|
||||||
right: 10.0,
|
fabLocation: FloatingActionButtonLocation.centerFloat,
|
||||||
left: 10.0,
|
showNotch: false,
|
||||||
child: new CenteredRow(
|
color: fires100,
|
||||||
// Fit sample:
|
// height: 170.0,
|
||||||
// https://github.com/apptreesoftware/flutter_map/blob/master/flutter_map_example/lib/pages/map_controller.dart
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: operation == MapOperation.subscriptionConfirm
|
actions: listWithoutNulls(<Widget>[
|
||||||
? <Widget>[
|
operation == FireMapStatus.subscriptionConfirm ||
|
||||||
new FireDistanceSlider(
|
numFires == null
|
||||||
initialValue: kmAround,
|
? null
|
||||||
onSlide: (distance) {
|
: new Text(numFires > 0
|
||||||
setState(() {
|
? S.of(context).firesAroundThisArea(
|
||||||
kmAround = distance;
|
numFires.toString(), kmAround.toString())
|
||||||
Debounce.seconds(1, updateFireStats);
|
: S
|
||||||
});
|
.of(context)
|
||||||
})
|
.noFiresAroundThisArea(kmAround.toString())),
|
||||||
]
|
SizedBox(width: 10.0)
|
||||||
: [],
|
])),
|
||||||
),
|
body: LayoutBuilder(
|
||||||
)
|
builder: (context, constraints) =>
|
||||||
]),
|
Stack(fit: StackFit.expand, children: <Widget>[
|
||||||
));
|
// Material(color: Colors.yellowAccent),
|
||||||
|
new Opacity(
|
||||||
|
opacity:
|
||||||
|
operation == FireMapStatus.subscriptionConfirm
|
||||||
|
? 0.5
|
||||||
|
: 1.0,
|
||||||
|
child: new FlutterMap(
|
||||||
|
options: mapOptions,
|
||||||
|
mapController: mapController,
|
||||||
|
layers: [
|
||||||
|
new TileLayerOptions(
|
||||||
|
maxZoom: 6.0,
|
||||||
|
subdomains: ['a', 'b', 'c'],
|
||||||
|
urlTemplate:
|
||||||
|
'https://tiles.wmflabs.org/bw-mapnik/{z}/{x}/{y}.png',
|
||||||
|
additionalOptions: {
|
||||||
|
// 'opacity': '0.1',
|
||||||
|
'attribution':
|
||||||
|
'© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
|
||||||
|
},
|
||||||
|
),
|
||||||
|
globals.isDevelopment
|
||||||
|
? new ZoomMapPluginOptions()
|
||||||
|
: new DummyMapPluginOptions(),
|
||||||
|
new MarkerLayerOptions(
|
||||||
|
markers: buildMarkers(location, this.fires,
|
||||||
|
this.industries, this.falsePos),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
Positioned(
|
||||||
|
top: constraints.maxHeight - 200,
|
||||||
|
right: 10.0,
|
||||||
|
left: 10.0,
|
||||||
|
child: new CenteredRow(
|
||||||
|
// Fit sample:
|
||||||
|
// https://github.com/apptreesoftware/flutter_map/blob/master/flutter_map_example/lib/pages/map_controller.dart
|
||||||
|
children: operation ==
|
||||||
|
FireMapStatus.subscriptionConfirm
|
||||||
|
? <Widget>[
|
||||||
|
new FireDistanceSlider(
|
||||||
|
initialValue: kmAround,
|
||||||
|
onSlide: (distance) {
|
||||||
|
setState(() {
|
||||||
|
kmAround = distance;
|
||||||
|
Debounce.seconds(1, updateFireStats);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
]
|
||||||
|
: [],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
]),
|
||||||
|
));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Marker> buildMarkers(YourLocation yourLocation, List<dynamic> fires,
|
List<Marker> buildMarkers(YourLocation yourLocation, List<dynamic> fires,
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,6 @@ import 'models/firesApi.dart';
|
||||||
import 'redux/fetchDataMiddleware.dart';
|
import 'redux/fetchDataMiddleware.dart';
|
||||||
import 'redux/reducers.dart';
|
import 'redux/reducers.dart';
|
||||||
import 'package:fires_flutter/models/yourLocationPersist.dart';
|
import 'package:fires_flutter/models/yourLocationPersist.dart';
|
||||||
import 'package:fires_flutter/models/yourLocation.dart';
|
|
||||||
|
|
||||||
Future<Map<String, dynamic>> loadSecrets() async {
|
Future<Map<String, dynamic>> loadSecrets() async {
|
||||||
return await SecretLoader(secretPath: 'assets/private-settings.json').load();
|
return await SecretLoader(secretPath: 'assets/private-settings.json').load();
|
||||||
|
|
|
||||||
|
|
@ -78,7 +78,12 @@ class AppState extends Object with _$AppStateSerializerMixin {
|
||||||
|
|
||||||
typedef void AddYourLocationFunction(YourLocation loc);
|
typedef void AddYourLocationFunction(YourLocation loc);
|
||||||
typedef void DeleteYourLocationFunction(ObjectId id);
|
typedef void DeleteYourLocationFunction(ObjectId id);
|
||||||
typedef void UpdateYourLocationFunction(ObjectId id, YourLocation loc);
|
|
||||||
typedef void ToggleSubscriptionFunction(YourLocation loc);
|
typedef void ToggleSubscriptionFunction(YourLocation loc);
|
||||||
typedef void SubscribeFunction(ObjectId id);
|
|
||||||
typedef void UnSubscribeFunction(ObjectId id);
|
typedef void OnLocationTapFunction(YourLocation loc);
|
||||||
|
typedef void OnSubscribeFunction(YourLocation loc);
|
||||||
|
typedef void OnUnSubscribeFunction(YourLocation loc);
|
||||||
|
typedef void OnSubscribeConfirmedFunction(YourLocation loc);
|
||||||
|
|
||||||
|
// unused
|
||||||
|
// typedef void UpdateYourLocationFunction(ObjectId id, YourLocation loc);
|
||||||
|
|
|
||||||
|
|
@ -1,27 +1,33 @@
|
||||||
import 'package:meta/meta.dart';
|
import 'package:meta/meta.dart';
|
||||||
|
import 'package:fires_flutter/models/yourLocation.dart';
|
||||||
enum FireMapOperation { view, subscriptionConfirm, unsubscribe }
|
import 'package:redux/redux.dart';
|
||||||
|
enum FireMapStatus { view, subscriptionConfirm, unsubscribe }
|
||||||
|
|
||||||
@immutable
|
@immutable
|
||||||
class FireMapState {
|
class FireMapState {
|
||||||
final FireMapOperation currentOperation;
|
final FireMapStatus status;
|
||||||
|
final YourLocation yourLocation;
|
||||||
|
|
||||||
const FireMapState.initial(): this.currentOperation = FireMapOperation.view;
|
const FireMapState.initial(): this.status = FireMapStatus.view, this.yourLocation = null;
|
||||||
|
|
||||||
FireMapState({this.currentOperation: FireMapOperation.view});
|
FireMapState({this.status: FireMapStatus.view, this.yourLocation});
|
||||||
|
|
||||||
|
FireMapState copyWith({FireMapStatus status, YourLocation yourLocation}) {
|
||||||
|
return new FireMapState(
|
||||||
|
yourLocation: yourLocation ?? this.yourLocation,
|
||||||
|
status: status ?? this.status);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) =>
|
bool operator ==(Object other) =>
|
||||||
identical(this, other) ||
|
identical(this, other) ||
|
||||||
other is FireMapState &&
|
other is FireMapState &&
|
||||||
runtimeType == other.runtimeType &&
|
runtimeType == other.runtimeType &&
|
||||||
currentOperation == other.currentOperation;
|
status == other.status &&
|
||||||
|
yourLocation == other.yourLocation;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode => currentOperation.hashCode;
|
int get hashCode =>
|
||||||
|
status.hashCode ^
|
||||||
FireMapState copyWith({FireMapOperation currentOperation}) {
|
yourLocation.hashCode;
|
||||||
return new FireMapState(
|
|
||||||
currentOperation: currentOperation ?? this.currentOperation);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,6 @@ class FiresApi {
|
||||||
var el = dataSubscriptions[i];
|
var el = dataSubscriptions[i];
|
||||||
var lat = el['location']['lat'];
|
var lat = el['location']['lat'];
|
||||||
var lon = el['location']['lon'];
|
var lon = el['location']['lon'];
|
||||||
var id = el['_id']['_str'];
|
|
||||||
subscribed.add(new YourLocation(
|
subscribed.add(new YourLocation(
|
||||||
id: ObjectId.fromHexString(el['_id']['_str']),
|
id: ObjectId.fromHexString(el['_id']['_str']),
|
||||||
lat: lat,
|
lat: lat,
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
import 'actions.dart';
|
import 'actions.dart';
|
||||||
import '../models/user.dart';
|
|
||||||
import '../models/appState.dart';
|
import '../models/appState.dart';
|
||||||
|
|
||||||
AppState appReducer(AppState state, action) {
|
AppState appReducer(AppState state, action) {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,20 @@
|
||||||
import '../models/fireMapState.dart';
|
import 'package:redux/redux.dart';
|
||||||
|
|
||||||
FireMapState fireMapReducer(FireMapState state, action) {
|
import '../models/fireMapState.dart';
|
||||||
return state;
|
import 'actions.dart';
|
||||||
}
|
|
||||||
|
final fireMapReducer = combineReducers<FireMapState>([
|
||||||
|
new TypedReducer<FireMapState, ShowYourLocationMapAction>(
|
||||||
|
_showYourLocationMap),
|
||||||
|
]);
|
||||||
|
|
||||||
|
FireMapState _showYourLocationMap(
|
||||||
|
FireMapState state, ShowYourLocationMapAction action) {
|
||||||
|
if (action.loc.subscribed) {
|
||||||
|
return state.copyWith(
|
||||||
|
status: action.loc.subscribed
|
||||||
|
? FireMapStatus.unsubscribe
|
||||||
|
: FireMapStatus.view,
|
||||||
|
yourLocation: action.loc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ import 'package:bson_objectid/bson_objectid.dart';
|
||||||
import 'package:fires_flutter/models/yourLocation.dart';
|
import 'package:fires_flutter/models/yourLocation.dart';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
abstract class YourLocationActions {}
|
abstract class YourLocationActions {}
|
||||||
|
|
||||||
class AddYourLocationAction extends YourLocationActions {
|
class AddYourLocationAction extends YourLocationActions {
|
||||||
|
|
@ -17,6 +16,12 @@ class AddedYourLocationAction extends YourLocationActions {
|
||||||
AddedYourLocationAction(this.loc);
|
AddedYourLocationAction(this.loc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ShowYourLocationMapAction extends YourLocationActions {
|
||||||
|
YourLocation loc;
|
||||||
|
|
||||||
|
ShowYourLocationMapAction(this.loc);
|
||||||
|
}
|
||||||
|
|
||||||
class DeleteYourLocationAction extends YourLocationActions {
|
class DeleteYourLocationAction extends YourLocationActions {
|
||||||
ObjectId id;
|
ObjectId id;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue