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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,4 +1,3 @@
import 'package:bson_objectid/bson_objectid.dart';
import 'package:comunes_flutter/comunes_flutter.dart';
import 'package:fires_flutter/models/yourLocation.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();
}
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
bool operator ==(Object other) =>
identical(this, other) ||

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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