Location edition

This commit is contained in:
vjrj 2018-07-05 12:50:08 +02:00
parent e55edce3d4
commit 87718b7ad5
15 changed files with 138 additions and 75 deletions

View file

@ -33,18 +33,25 @@ class _ViewModel {
@required this.yourLocations}); @required this.yourLocations});
@override @override
bool operator ==(Object other) => bool operator ==(Object other) {
identical(this, other) || return identical(this, other) ||
other is _ViewModel && other is _ViewModel &&
runtimeType == other.runtimeType && runtimeType == other.runtimeType &&
yourLocations == other.yourLocations; yourLocations == other.yourLocations;
}
@override @override
int get hashCode => yourLocations.hashCode; int get hashCode => yourLocations.hashCode;
} }
class ActiveFiresPage extends StatelessWidget { 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>(); final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
@ -134,6 +141,7 @@ class ActiveFiresPage extends StatelessWidget {
return new StoreConnector<AppState, _ViewModel>( return new StoreConnector<AppState, _ViewModel>(
distinct: true, distinct: true,
converter: (store) { converter: (store) {
print('New ViewModel of Active Fires');
return new _ViewModel( return new _ViewModel(
onAdd: (loc) { onAdd: (loc) {
if (store.state.yourLocations if (store.state.yourLocations

View file

@ -31,7 +31,10 @@ class FiresApp extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return new StoreProvider( StatefulWidget home = new MaterialAppWithIntroHome(
introWidget, continueWidget, 'showInitialWizard-2018-06-27-01');
return new StoreProvider<AppState>(
store: this.store, store: this.store,
child: new MaterialApp( child: new MaterialApp(
localizationsDelegates: [ localizationsDelegates: [
@ -42,8 +45,7 @@ class FiresApp extends StatelessWidget {
supportedLocales: S.delegate.supportedLocales, supportedLocales: S.delegate.supportedLocales,
localeResolutionCallback: localeResolutionCallback:
S.delegate.resolution(fallback: new Locale("en", "")), S.delegate.resolution(fallback: new Locale("en", "")),
home: new MaterialAppWithIntroHome( home: home,
introWidget, continueWidget, 'showInitialWizard-2018-06-27-01'),
onGenerateTitle: (context) { onGenerateTitle: (context) {
print('MaterialApp onGenerateTitle'); print('MaterialApp onGenerateTitle');
if (store.state.user.lang == null) { if (store.state.user.lang == null) {

View file

@ -1,13 +1,14 @@
import 'package:flutter/material.dart';
import 'dart:convert'; import 'dart:convert';
import 'globals.dart' as globals;
import 'package:http/http.dart' as http;
import 'package:comunes_flutter/comunes_flutter.dart'; import 'package:comunes_flutter/comunes_flutter.dart';
import 'customBottomAppBar.dart'; import 'package:flutter/material.dart';
import 'colors.dart';
import 'generated/i18n.dart';
import 'customMoment.dart';
import 'package:flutter_simple_dependency_injection/injector.dart'; import 'package:flutter_simple_dependency_injection/injector.dart';
import 'package:http/http.dart' as http;
import 'colors.dart';
import 'customBottomAppBar.dart';
import 'customMoment.dart';
import 'generated/i18n.dart';
class GlobalFiresBottomStats extends StatefulWidget { class GlobalFiresBottomStats extends StatefulWidget {
@override @override
@ -22,7 +23,7 @@ class _GlobalFiresBottomStatsState extends State<GlobalFiresBottomStats> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
/* http.read('${firesApiUrl}status/last-fire-check').then((result) { http.read('${firesApiUrl}status/last-fire-check').then((result) {
try { try {
var now = Moment.now(); var now = Moment.now();
var last = DateTime.parse(json.decode(result)['value']); var last = DateTime.parse(json.decode(result)['value']);
@ -42,27 +43,29 @@ class _GlobalFiresBottomStatsState extends State<GlobalFiresBottomStats> {
print('Cannot get the last fire check'); print('Cannot get the last fire check');
print(e); print(e);
} }
}); */ });
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return new CustomBottomAppBar( return new CustomBottomAppBar(
fabLocation: FloatingActionButtonLocation.centerDocked, fabLocation: FloatingActionButtonLocation.centerDocked,
showNotch: true, showNotch: true,
color: fires100, color: fires100,
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
actions: listWithoutNulls(<Widget>[ actions: listWithoutNulls(<Widget>[
activeFires > 0 && lastCheck != null activeFires > 0 && lastCheck != null
? new Column( ? new Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
new Text(S.of(context).activeFiresWorldWide(activeFires.toString())), new Text(S
new Text(S.of(context).updatedLastCheck(lastCheck)) .of(context)
]) .activeFiresWorldWide(activeFires.toString())),
: null, new Text(S.of(context).updatedLastCheck(lastCheck))
SizedBox(width: 10.0) ])
])); : null,
SizedBox(width: 10.0)
]));
} }
} }

View file

@ -41,7 +41,8 @@ void mainCommon(List<Middleware<AppState>> otherMiddleware) {
}); });
// Listen to store changes, and re-render when the state is updated // Listen to store changes, and re-render when the state is updated
// store.onChange.listen((state) { store.onChange.listen((state) {
// }); // print('Store onChange');
});
}); });
} }

View file

@ -14,7 +14,7 @@ void main() {
dynamic action, dynamic action,
DateTime timestamp, DateTime timestamp,
) { ) {
return "${action.toString().replaceAll('Instance of ', '')}"; return ">>>>> ${action.toString().replaceAll('Instance of ', '')}";
} }
LogLevel logRedux = LogLevel.actions; LogLevel logRedux = LogLevel.actions;

View file

@ -19,6 +19,7 @@ Widget mainDrawer(BuildContext context) {
children: listWithoutNulls(<Widget>[ children: listWithoutNulls(<Widget>[
new GestureDetector( new GestureDetector(
onTap: () { onTap: () {
Navigator.pop(context);
Navigator.pushNamed(context, '/'); Navigator.pushNamed(context, '/');
}, },
child: new DrawerHeader( child: new DrawerHeader(
@ -46,6 +47,7 @@ Widget mainDrawer(BuildContext context) {
leading: const Icon(Icons.whatshot), leading: const Icon(Icons.whatshot),
title: new Text(S.of(context).activeFires), title: new Text(S.of(context).activeFires),
onTap: () { onTap: () {
Navigator.pop(context);
Navigator.pushNamed(context, ActiveFiresPage.routeName); Navigator.pushNamed(context, ActiveFiresPage.routeName);
}, },
), ),
@ -54,6 +56,7 @@ Widget mainDrawer(BuildContext context) {
title: new Text(S.of(context).notifyAFire), title: new Text(S.of(context).notifyAFire),
onTap: () { onTap: () {
// Then close the drawer // Then close the drawer
Navigator.pop(context);
Navigator.pushNamed(context, Sandbox.routeName); Navigator.pushNamed(context, Sandbox.routeName);
}, },
), ),
@ -63,6 +66,7 @@ Widget mainDrawer(BuildContext context) {
title: new Text(S.of(context).supportThisInitiative), title: new Text(S.of(context).supportThisInitiative),
onTap: () { onTap: () {
// Then close the drawer // Then close the drawer
Navigator.pop(context);
Navigator.pushNamed(context, '/subscriptions'); Navigator.pushNamed(context, '/subscriptions');
}, },
), ),
@ -71,6 +75,7 @@ Widget mainDrawer(BuildContext context) {
leading: const Icon(Icons.bug_report), leading: const Icon(Icons.bug_report),
title: new Text('Sandbox'), title: new Text('Sandbox'),
onTap: () { onTap: () {
Navigator.pop(context);
Navigator.pushNamed(context, Sandbox.routeName); Navigator.pushNamed(context, Sandbox.routeName);
}, },
): null, ): null,

View file

@ -1,4 +1,3 @@
import 'package:bson_objectid/bson_objectid.dart';
import 'package:comunes_flutter/comunes_flutter.dart'; import 'package:comunes_flutter/comunes_flutter.dart';
import 'package:fires_flutter/models/yourLocation.dart'; import 'package:fires_flutter/models/yourLocation.dart';
import 'package:json_annotation/json_annotation.dart'; import 'package:json_annotation/json_annotation.dart';

View file

@ -39,6 +39,24 @@ class YourLocation extends Object with _$YourLocationSerializerMixin {
if (this.id == null) this.id = new ObjectId(); if (this.id == null) this.id = new ObjectId();
} }
YourLocation copyWith(
{id,
lat,
lon,
description,
distance,
subscribed}) {
return new YourLocation(
id: id?? this.id,
lat: lat?? this.lat,
lon: lon?? this.lon,
description: description?? this.description,
distance: distance?? this.distance,
subscribed: subscribed?? this.subscribed
);
}
@override @override
bool operator ==(Object other) => bool operator ==(Object other) =>
identical(this, other) || identical(this, other) ||

View file

@ -19,23 +19,16 @@ YourLocation _$YourLocationFromJson(Map<String, dynamic> json) =>
abstract class _$YourLocationSerializerMixin { abstract class _$YourLocationSerializerMixin {
ObjectId get id; ObjectId get id;
double get lat; double get lat;
double get lon; double get lon;
String get description; String get description;
bool get subscribed; bool get subscribed;
int get distance; int get distance;
Map<String, dynamic> toJson() => new _$YourLocationJsonMapWrapper(this); Map<String, dynamic> toJson() => new _$YourLocationJsonMapWrapper(this);
} }
class _$YourLocationJsonMapWrapper extends $JsonMapWrapper { class _$YourLocationJsonMapWrapper extends $JsonMapWrapper {
final _$YourLocationSerializerMixin _v; final _$YourLocationSerializerMixin _v;
_$YourLocationJsonMapWrapper(this._v); _$YourLocationJsonMapWrapper(this._v);
@override @override

View file

@ -25,6 +25,7 @@ Future<List<YourLocation>> loadYourLocations() async {
} }
persistYourLocations(List<YourLocation> yl) { persistYourLocations(List<YourLocation> yl) {
print('Persisting $yl');
globals.prefs.then((prefs) { globals.prefs.then((prefs) {
List<String> ylAsString = []; List<String> ylAsString = [];
yl.forEach((location) { yl.forEach((location) {

View file

@ -41,15 +41,16 @@ void fetchYourLocationsMiddleware(
// FXIME save lat/lon // FXIME save lat/lon
} else { } else {
// No subscribed (only local) // No subscribed (only local)
store.dispatch(new UpdateYourLocationAction(action.loc));
persistYourLocations(store.state.yourLocations);
} }
} }
if (action is AddYourLocationAction) { if (action is AddYourLocationAction) {
if (action.loc.subscribed) { if (action.loc.subscribed) {
subscribeViaApi(store, action.loc, subscribeViaApi(store, action.loc,
(sub) => store.dispatch(new AddedYourLocationAction(sub))); (sub) {
store.dispatch(new AddedYourLocationAction(sub));
persistYourLocations(store.state.yourLocations);
});
} else { } else {
// No subscribed (only local) // No subscribed (only local)
store.dispatch(new AddedYourLocationAction(action.loc)); store.dispatch(new AddedYourLocationAction(action.loc));
@ -73,7 +74,6 @@ void fetchYourLocationsMiddleware(
.getYourLocationFireStats(store.state, action.loc) .getYourLocationFireStats(store.state, action.loc)
.then((result) => store.dispatch(result)); .then((result) => store.dispatch(result));
} }
if (action is UpdateYourLocationAction) { if (action is UpdateYourLocationAction) {
if (action.loc.subscribed) if (action.loc.subscribed)
Debounce.seconds( Debounce.seconds(
@ -84,6 +84,8 @@ void fetchYourLocationsMiddleware(
else { else {
// FIXME do something? // FIXME do something?
} }
store.dispatch(new UpdatedYourLocationAction(action.loc));
persistYourLocations(store.state.yourLocations);
} }
if (action is SubscribeConfirmAction) { if (action is SubscribeConfirmAction) {

View file

@ -1,6 +1,5 @@
import 'package:bson_objectid/bson_objectid.dart'; import 'package:bson_objectid/bson_objectid.dart';
import 'package:fires_flutter/models/yourLocation.dart'; import 'package:fires_flutter/models/yourLocation.dart';
import 'package:meta/meta.dart';
abstract class YourLocationActions {} abstract class YourLocationActions {}
@ -34,6 +33,12 @@ class UpdateYourLocationAction extends YourLocationActions {
UpdateYourLocationAction(this.loc); UpdateYourLocationAction(this.loc);
} }
class UpdatedYourLocationAction extends YourLocationActions {
YourLocation loc;
UpdatedYourLocationAction(this.loc);
}
class ToggleSubscriptionAction extends YourLocationActions { class ToggleSubscriptionAction extends YourLocationActions {
YourLocation loc; YourLocation loc;

View file

@ -8,8 +8,8 @@ final yourLocationsReducer = combineReducers<List<YourLocation>>([
_addedYourLocation), _addedYourLocation),
new TypedReducer<List<YourLocation>, DeletedYourLocationAction>( new TypedReducer<List<YourLocation>, DeletedYourLocationAction>(
_deletedYourLocation), _deletedYourLocation),
new TypedReducer<List<YourLocation>, UpdateYourLocationAction>( new TypedReducer<List<YourLocation>, UpdatedYourLocationAction>(
_updateYourLocation), _updatedYourLocation),
new TypedReducer<List<YourLocation>, ToggledSubscriptionAction>( new TypedReducer<List<YourLocation>, ToggledSubscriptionAction>(
_toggledSubscriptionAction) _toggledSubscriptionAction)
]); ]);
@ -26,8 +26,8 @@ List<YourLocation> _deletedYourLocation(
.toList(); .toList();
} }
List<YourLocation> _updateYourLocation( List<YourLocation> _updatedYourLocation(
List<YourLocation> yourLocations, UpdateYourLocationAction action) { List<YourLocation> yourLocations, UpdatedYourLocationAction action) {
return yourLocations return yourLocations
.map((yourLocation) => .map((yourLocation) =>
yourLocation.id == action.loc.id ? action.loc : yourLocation) yourLocation.id == action.loc.id ? action.loc : yourLocation)

View file

@ -15,6 +15,7 @@ class Sandbox extends StatelessWidget {
return Scaffold( return Scaffold(
appBar: new AppBar( appBar: new AppBar(
title: new TextField( title: new TextField(
autofocus: true,
controller: new TextEditingController(text: "kk"), controller: new TextEditingController(text: "kk"),
decoration: new InputDecoration(), decoration: new InputDecoration(),
onSubmitted: (todoText) {}, onSubmitted: (todoText) {},
@ -23,4 +24,3 @@ class Sandbox extends StatelessWidget {
); );
} }
} }
-

View file

@ -54,9 +54,17 @@ class _ViewModel {
int get hashCode => mapState.hashCode; int get hashCode => mapState.hashCode;
} }
class YourLocationMap extends StatelessWidget { class YourLocationMap extends StatefulWidget {
@override
_YourLocationMapState createState() => _YourLocationMapState();
}
class _YourLocationMapState extends State<YourLocationMap> {
// 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 GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
final FirebaseMessaging _firebaseMessaging = new FirebaseMessaging(); final FirebaseMessaging _firebaseMessaging = new FirebaseMessaging();
YourLocation editingLocation;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -80,16 +88,19 @@ class YourLocationMap extends StatelessWidget {
store.dispatch(new UpdateYourLocationMapAction(loc)); store.dispatch(new UpdateYourLocationMapAction(loc));
}, },
onEdit: (loc) => store.dispatch(new EditYourLocationAction(loc)), onEdit: (loc) => store.dispatch(new EditYourLocationAction(loc)),
onEditConfirm: (loc) => onEditConfirm: (loc) {
store.dispatch(new EditConfirmYourLocationAction(loc)), store.dispatch(new UpdateYourLocationAction(loc));
store.dispatch(new UpdateYourLocationMapAction(loc));
store.dispatch(new EditConfirmYourLocationAction(loc));
},
mapState: store.state.fireMapState); mapState: store.state.fireMapState);
}, },
builder: (context, view) { builder: (context, view) {
print('New map builder');
YourLocation location = view.mapState.yourLocation; YourLocation location = view.mapState.yourLocation;
editingLocation = location.copyWith();
print('New map builder with ${editingLocation.description}');
assert(location != null); assert(location != null);
TextEditingController textController =
new TextEditingController(text: location.description);
MapOptions mapOptions = new MapOptions( MapOptions mapOptions = new MapOptions(
center: new LatLng(location.lat, location.lon), center: new LatLng(location.lat, location.lon),
@ -151,29 +162,23 @@ class YourLocationMap extends StatelessWidget {
// Do something with it // Do something with it
return new Scaffold( return new Scaffold(
key: _scaffoldKey, key: _scaffoldKey,
resizeToAvoidBottomPadding: true,
appBar: new AppBar( appBar: new AppBar(
title: status == FireMapStatus.edit title: status == FireMapStatus.edit
? new TextField( ? new TextField(
// autofocus: true, // autofocus: true,
key: new Key('LocationDescField'), key: new Key('LocationDescField'),
keyboardType: TextInputType.text, keyboardType: TextInputType.text,
controller: textController,
decoration: new InputDecoration(), decoration: new InputDecoration(),
/* onSubmitted: (newDesc) { controller: new TextEditingController.fromValue(new TextEditingValue(text: editingLocation.description,selection: new TextSelection.collapsed(offset: editingLocation.description.length-1))),
// location.description = newDesc; onChanged: (newDesc) {
// view.onEditConfirm(location); editingLocation =
// view.editController.text = ''; location.copyWith(description: newDesc);
},*/ },
) )
: new Text(location.description), : new Text(location.description),
actions: listWithoutNulls(<Widget>[ actions: buildAppBarActions(status, view, location),
status == FireMapStatus.view ||
status == FireMapStatus.unsubscribe
? new IconButton(
icon: new Icon(Icons.edit),
onPressed: () => view.onEdit(location))
: null
]),
), ),
floatingActionButton: status == FireMapStatus.edit floatingActionButton: status == FireMapStatus.edit
? null ? null
@ -239,6 +244,27 @@ class YourLocationMap extends StatelessWidget {
}); });
} }
List<Widget> buildAppBarActions(
FireMapStatus status, _ViewModel view, YourLocation location) {
switch (status) {
case FireMapStatus.view:
case FireMapStatus.unsubscribe:
return <Widget>[
new IconButton(
icon: new Icon(Icons.edit),
onPressed: () => view.onEdit(location))
];
case FireMapStatus.edit:
return <Widget>[
new IconButton(
icon: new Icon(Icons.save),
onPressed: () => view.onEditConfirm(editingLocation))
];
default:
return <Widget>[];
}
}
List<Marker> buildMarkers(YourLocation yourLocation, List<dynamic> fires, List<Marker> buildMarkers(YourLocation yourLocation, List<dynamic> fires,
List<dynamic> falsePos, List<dynamic> industries) { List<dynamic> falsePos, List<dynamic> industries) {
List<Marker> markers = []; List<Marker> markers = [];