From 5d947f957784cf571b657a6c740c87feae83526a Mon Sep 17 00:00:00 2001 From: vjrj Date: Wed, 27 Jun 2018 22:29:54 +0200 Subject: [PATCH] More redux work --- lib/activeFires.dart | 182 ++++++++++++++++++---------- lib/firebaseMessagingConf.dart | 2 - lib/globals.dart | 3 - lib/mainCommon.dart | 2 +- lib/models/appState.dart | 23 +++- lib/models/firesApi.dart | 4 +- lib/models/yourLocation.dart | 8 ++ lib/models/yourLocationPersist.dart | 15 +-- lib/redux/appActions.dart | 4 +- lib/redux/fetchDataMiddleware.dart | 4 +- lib/redux/loadedReducer.dart | 3 + lib/redux/loadingReducer.dart | 2 + lib/redux/yourLocationActions.dart | 4 +- lib/redux/yourLocationsReducer.dart | 2 +- 14 files changed, 170 insertions(+), 88 deletions(-) diff --git a/lib/activeFires.dart b/lib/activeFires.dart index 5337e9d..c9e2ba5 100644 --- a/lib/activeFires.dart +++ b/lib/activeFires.dart @@ -1,19 +1,50 @@ import 'dart:async'; import 'package:comunes_flutter/comunes_flutter.dart'; +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 'colors.dart'; import 'generated/i18n.dart'; import 'genericMap.dart'; import 'globalFiresBottomStats.dart'; -import 'globals.dart' as globals; import 'locationUtils.dart'; import 'mainDrawer.dart'; +import 'models/appState.dart'; import 'placesAutocompleteUtils.dart'; -import 'package:fires_flutter/models/yourLocation.dart'; -import 'package:fires_flutter/models/yourLocationPersist.dart'; +import 'redux/actions.dart'; + +class _ViewModel { + final List yourLocations; + final AddYourLocationFunction onAdd; + final DeleteYourLocationFunction onDelete; + final ToggleSubscriptionFunction onToggleSubs; + + _ViewModel( + {@required this.onAdd, + @required this.onDelete, + @required this.onToggleSubs, + @required this.yourLocations}); + + @override + bool operator ==(Object other) => + identical(this, other) || + other is _ViewModel && + runtimeType == other.runtimeType && + yourLocations == other.yourLocations && + onAdd == other.onAdd && + onDelete == other.onDelete && + onToggleSubs == other.onToggleSubs; + + @override + int get hashCode => + yourLocations.hashCode ^ + onAdd.hashCode ^ + onDelete.hashCode ^ + onToggleSubs.hashCode; +} class ActiveFiresPage extends StatefulWidget { static const String routeName = '/fires'; @@ -27,7 +58,7 @@ class ActiveFiresPage extends StatefulWidget { class _ActiveFiresPageState extends State { final GlobalKey _scaffoldKey = new GlobalKey(); - List _fabMiniMenuItemList() { + List _fabMiniMenuItemList(AddYourLocationFunction onAdd) { return [ new FabMiniMenuItem.withText( new Icon(Icons.location_searching), @@ -35,7 +66,7 @@ class _ActiveFiresPageState extends State { 8.0, S.of(context).addYourCurrentPosition, () { - onAddYourLocation(); + onAddYourLocation(onAdd); }, S.of(context).addYourCurrentPosition, Colors.black38, @@ -43,14 +74,14 @@ class _ActiveFiresPageState extends State { ), new FabMiniMenuItem.withText(new Icon(Icons.edit_location), fires600, 8.0, S.of(context).addSomePlace, () { - onAddOtherLocation(); + onAddOtherLocation(onAdd); }, S.of(context).addSomePlace, Colors.black38, Colors.white) ]; } _ActiveFiresPageState(); - Widget _buildRow(YourLocation loc) { + Widget _buildRow(YourLocation loc, onToggle) { return new ListTile( dense: true, leading: const Icon(Icons.location_on), @@ -60,10 +91,11 @@ class _ActiveFiresPageState extends State { : Icons.notifications_off), onPressed: () { loc.subscribed = !loc.subscribed; - int i = globals.yourLocations.indexOf(loc); + onToggle(loc.id); + /* FIXME int i = globals.yourLocations.indexOf(loc); globals.yourLocations.removeAt(i); globals.yourLocations.insert(i, loc); - persistYourLocations(); + persistYourLocations(); */ setState(() {}); }), title: new Text(loc.description), @@ -92,31 +124,31 @@ class _ActiveFiresPageState extends State { )); } - Widget _buildSavedLocations() { + Widget _buildSavedLocations(List yl, onDeleted, onToggle) { return new ListView.builder( padding: const EdgeInsets.all(16.0), - itemCount: globals.yourLocations.length, + itemCount: yl.length, itemBuilder: (BuildContext _context, int i) { - return _buildItem(globals.yourLocations.elementAt(i)); + return _buildItem(yl.elementAt(i), onDeleted, onToggle); }); } void handleUndo(YourLocation item) { setState(() { + /* FIXME globals.yourLocations.add(item); - persistYourLocations(); + persistYourLocations(); */ }); } - Widget _buildItem(YourLocation item) { + Widget _buildItem(YourLocation item, onDelete, onToggle) { final ThemeData theme = Theme.of(context); return new Dismissible( key: new ObjectKey(item), direction: DismissDirection.horizontal, onDismissed: (DismissDirection direction) { setState(() { - globals.yourLocations.remove(item); - persistYourLocations(); + onDelete(item.id); }); _scaffoldKey.currentState.showSnackBar(new SnackBar( @@ -142,67 +174,89 @@ class _ActiveFiresPageState extends State { color: theme.canvasColor, border: new Border( bottom: new BorderSide(color: theme.dividerColor))), - child: _buildRow(item))); + child: _buildRow(item, onToggle))); } @override Widget build(BuildContext context) { - var hasLocations = globals.yourLocations.length > 0; - final title = hasLocations - ? S.of(context).firesInYourPlaces - : S.of(context).firesInTheWorld; - print('Building Active Fires'); - return Scaffold( - key: _scaffoldKey, - // FIXME new? - drawer: new MainDrawer(context), - appBar: new AppBar( - title: Text(title), - leading: IconButton( - icon: Icon(Icons.menu), - onPressed: () { - _scaffoldKey.currentState.openDrawer(); - }, - ), - ), - floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat, - bottomNavigationBar: new GlobalFiresBottomStats(), - body: hasLocations - ? new Stack(children: [ - _buildSavedLocations(), - new FabDialer( - _fabMiniMenuItemList(), fires600, new Icon(Icons.add)) - ]) - : new Center( - child: new CenteredColumn(children: [ - new RoundedBtn( - icon: Icons.location_searching, - text: 'Fires near your', - onPressed: onAddYourLocation, - backColor: fires600), - const SizedBox(height: 26.0), - new RoundedBtn( - icon: Icons.edit_location, - text: S.of(context).firesNearPlace, - onPressed: onAddOtherLocation, - backColor: fires600), - ])), - ); + return new StoreConnector( + distinct: false, // FIXME + converter: (store) { + return new _ViewModel( + onAdd: (loc) { + store.dispatch(new AddYourLocationAction(loc)); + }, + onDelete: (id) { + store.dispatch(new DeleteYourLocationAction(id)); + }, + onToggleSubs: (id) { + store.dispatch(new ToggleSubscriptionAction(id)); + }, + yourLocations: store.state.yourLocations); + }, + builder: (context, view) { + var hasLocations = view.yourLocations.length > 0; + final title = hasLocations + ? S.of(context).firesInYourPlaces + : S.of(context).firesInTheWorld; + print('Building Active Fires'); + + return Scaffold( + key: _scaffoldKey, + // FIXME new? + drawer: new MainDrawer(context), + appBar: new AppBar( + title: Text(title), + leading: IconButton( + icon: Icon(Icons.menu), + onPressed: () { + _scaffoldKey.currentState.openDrawer(); + }, + ), + ), + floatingActionButtonLocation: + FloatingActionButtonLocation.centerFloat, + bottomNavigationBar: new GlobalFiresBottomStats(), + body: hasLocations + ? new Stack(children: [ + _buildSavedLocations( + view.yourLocations, view.onDelete, view.onToggleSubs), + new FabDialer(_fabMiniMenuItemList(view.onAdd), fires600, + new Icon(Icons.add)) + ]) + : new Center( + child: new CenteredColumn(children: [ + new RoundedBtn( + icon: Icons.location_searching, + text: 'Fires near your', + onPressed: () => onAddYourLocation(view.onAdd), + backColor: fires600), + const SizedBox(height: 26.0), + new RoundedBtn( + icon: Icons.edit_location, + text: S.of(context).firesNearPlace, + onPressed: () => onAddOtherLocation(view.onAdd), + backColor: fires600), + ])), + ); + }); } - void onAddYourLocation() { + void onAddYourLocation(AddYourLocationFunction onAdd) { Future location = getUserLocation(_scaffoldKey); - _saveLocation(location); + _saveLocation(location, onAdd); } - void onAddOtherLocation() { + void onAddOtherLocation(AddYourLocationFunction onAdd) { Future location = openPlacesDialog(_scaffoldKey); - _saveLocation(location); + _saveLocation(location, onAdd); } - void _saveLocation(Future location) { + void _saveLocation(Future location, onAdd) { location.then((newLocation) { if (newLocation != YourLocation.noLocation) { + onAdd(newLocation); + /* FIXME if (globals.yourLocations.contains(newLocation)) { showSnackMsg(S.of(context).addedThisLocation); } else @@ -212,7 +266,7 @@ class _ActiveFiresPageState extends State { new Timer(new Duration(milliseconds: 1000), () { showLocationMap(newLocation); }); - }); + }); */ } }); } diff --git a/lib/firebaseMessagingConf.dart b/lib/firebaseMessagingConf.dart index 74c7ba6..b4066de 100644 --- a/lib/firebaseMessagingConf.dart +++ b/lib/firebaseMessagingConf.dart @@ -3,8 +3,6 @@ import 'package:redux/src/store.dart'; import 'models/appState.dart'; import 'redux/actions.dart'; -import 'globals.dart' as globals; -import 'models/firesApi.dart'; final FirebaseMessaging _firebaseMessaging = new FirebaseMessaging(); diff --git a/lib/globals.dart b/lib/globals.dart index fe41b69..2ce4a4c 100644 --- a/lib/globals.dart +++ b/lib/globals.dart @@ -5,8 +5,6 @@ import 'package:get_it/get_it.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'dart:async'; -import 'package:fires_flutter/models/yourLocation.dart'; - // FIXME remove this later String gmapKey; String firesApiKey; @@ -18,6 +16,5 @@ final Widget appMediumIcon = final Widget appIcon = Image.asset('images/logo-200.png', width: 24.0, height: 24.0); final Future prefs = SharedPreferences.getInstance(); -List yourLocations = []; final GetIt getIt = new GetIt(); bool isDevelopment = false; diff --git a/lib/mainCommon.dart b/lib/mainCommon.dart index 86b7c61..8989758 100644 --- a/lib/mainCommon.dart +++ b/lib/mainCommon.dart @@ -12,7 +12,7 @@ import 'models/firesApi.dart'; import 'redux/fetchDataMiddleware.dart'; import 'redux/reducers.dart'; import 'package:fires_flutter/models/yourLocationPersist.dart'; -import 'package:redux_logging/redux_logging.dart'; + Future> loadSecrets() async { return await SecretLoader(secretPath: 'assets/private-settings.json').load(); } diff --git a/lib/models/appState.dart b/lib/models/appState.dart index 9679aa8..333c251 100644 --- a/lib/models/appState.dart +++ b/lib/models/appState.dart @@ -1,9 +1,12 @@ +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'; import 'package:meta/meta.dart'; -import 'package:comunes_flutter/comunes_flutter.dart'; + import 'fireMapState.dart'; -import 'yourLocation.dart'; import 'user.dart'; + part 'appState.g.dart'; @immutable @@ -32,8 +35,8 @@ class AppState extends Object with _$AppStateSerializerMixin { _$AppStateFromJson(json); AppState( - {this.yourLocations : const [], - this.user: const User.initial(), + {this.yourLocations: const [], + this.user: const User.initial(), this.isLoading: false, this.isLoaded: false, this.error: null, @@ -66,6 +69,16 @@ class AppState extends Object with _$AppStateSerializerMixin { @override String toString() { - return 'AppState{\nuser: ${user}\nisLoading: $isLoading\nisLoaded: $isLoaded\napiKey: ${ellipse(firesApiKey, 8)}\napiUrl: ${ellipse(firesApiUrl, 8)}\nyourLocations: ${yourLocations}'; + return 'AppState{\nuser: ${user}\nisLoading: $isLoading\nisLoaded: $isLoaded\napiKey: ${ellipse( + firesApiKey, 8)}\napiUrl: ${ellipse( + firesApiUrl, 8)}\nyourLocations count: ${yourLocations + .length}\nyourLocations: ${yourLocations}'; } } + +typedef void AddYourLocationFunction(YourLocation loc); +typedef void DeleteYourLocationFunction(ObjectId id); +typedef void UpdateYourLocationFunction(ObjectId id, YourLocation loc); +typedef void ToggleSubscriptionFunction(ObjectId id); +typedef void SubscribeFunction(ObjectId id); +typedef void UnSubscribeFunction(ObjectId id); diff --git a/lib/models/firesApi.dart b/lib/models/firesApi.dart index df7b703..761631d 100644 --- a/lib/models/firesApi.dart +++ b/lib/models/firesApi.dart @@ -4,8 +4,10 @@ import 'dart:convert'; import 'package:http/http.dart' as ht; import 'package:jaguar_resty/jaguar_resty.dart' as resty; +import 'package:fires_flutter/models/yourLocation.dart'; + + import 'appState.dart'; -import 'yourLocation.dart'; class FiresApi { FiresApi() { diff --git a/lib/models/yourLocation.dart b/lib/models/yourLocation.dart index 671839d..a3e61be 100644 --- a/lib/models/yourLocation.dart +++ b/lib/models/yourLocation.dart @@ -37,6 +37,8 @@ class YourLocation extends Object with _$YourLocationSerializerMixin { if (this.id == null) this.id = new ObjectId(); } + + @override bool operator ==(Object other) => identical(this, other) || @@ -55,4 +57,10 @@ class YourLocation extends Object with _$YourLocationSerializerMixin { lon.hashCode ^ description.hashCode ^ subscribed.hashCode; + + @override + String toString() { + return 'YourLocation {id: $id, lat: $lat, lon: $lon, description: $description, subscribed: $subscribed}'; + } + } diff --git a/lib/models/yourLocationPersist.dart b/lib/models/yourLocationPersist.dart index 863d17e..7b9973f 100644 --- a/lib/models/yourLocationPersist.dart +++ b/lib/models/yourLocationPersist.dart @@ -1,11 +1,12 @@ -import 'dart:convert'; -import '../globals.dart' as globals; import 'dart:async'; -import 'yourLocation.dart'; +import 'dart:convert'; + +import 'package:fires_flutter/models/yourLocation.dart'; import 'package:shared_preferences/shared_preferences.dart'; -final String locationKey = 'yourlocations'; +import '../globals.dart' as globals; +final String locationKey = 'yourlocations'; Future> loadYourLocations() async { return await globals.prefs.then((prefs) { @@ -23,7 +24,8 @@ persistYourLocations(List yl) { }); } -Future> loadYourLocationsWithPrefs(SharedPreferences prefs) async { +Future> loadYourLocationsWithPrefs( + SharedPreferences prefs) async { return await globals.prefs.then((prefs) { List yourLocations = prefs.getStringList(locationKey); if (yourLocations == null) { @@ -33,9 +35,8 @@ Future> loadYourLocationsWithPrefs(SharedPreferences prefs) a List persistedList = List(); yourLocations.forEach((locationString) { Map locationMap = json.decode(locationString); - persistedList.add(YourLocation.fromJson(locationMap)); + persistedList.add(YourLocation.fromJson(locationMap)); }); return persistedList; }); - } diff --git a/lib/redux/appActions.dart b/lib/redux/appActions.dart index e0709ad..4c6421a 100644 --- a/lib/redux/appActions.dart +++ b/lib/redux/appActions.dart @@ -1,4 +1,4 @@ -import '../models/yourLocation.dart'; +import 'package:fires_flutter/models/yourLocation.dart'; abstract class AppActions {} @@ -32,4 +32,4 @@ class OnUserLangAction extends AppActions { final String lang; OnUserLangAction(this.lang); -} \ No newline at end of file +} diff --git a/lib/redux/fetchDataMiddleware.dart b/lib/redux/fetchDataMiddleware.dart index 10e419c..3c7a68d 100644 --- a/lib/redux/fetchDataMiddleware.dart +++ b/lib/redux/fetchDataMiddleware.dart @@ -3,7 +3,9 @@ import 'package:redux/redux.dart'; import '../globals.dart' as globals; import '../models/appState.dart'; import '../models/firesApi.dart'; -import '../models/yourLocation.dart'; + +import 'package:fires_flutter/models/yourLocation.dart'; + import '../models/yourLocationPersist.dart'; import 'actions.dart'; diff --git a/lib/redux/loadedReducer.dart b/lib/redux/loadedReducer.dart index 80e5026..96b549a 100644 --- a/lib/redux/loadedReducer.dart +++ b/lib/redux/loadedReducer.dart @@ -1,3 +1,6 @@ +import 'actions.dart'; + bool loadedReducer(isLoaded, action) { + if (action is FetchYourLocationsSucceededAction) return true; return isLoaded; } \ No newline at end of file diff --git a/lib/redux/loadingReducer.dart b/lib/redux/loadingReducer.dart index a5b7e16..bbb62cc 100644 --- a/lib/redux/loadingReducer.dart +++ b/lib/redux/loadingReducer.dart @@ -1,3 +1,5 @@ +import 'actions.dart'; bool loadingReducer(isLoading, action) { + if (action is FetchYourLocationsAction) return true; return isLoading; } \ No newline at end of file diff --git a/lib/redux/yourLocationActions.dart b/lib/redux/yourLocationActions.dart index 1af9878..1467b37 100644 --- a/lib/redux/yourLocationActions.dart +++ b/lib/redux/yourLocationActions.dart @@ -1,6 +1,7 @@ import 'package:bson_objectid/bson_objectid.dart'; +import 'package:fires_flutter/models/yourLocation.dart'; + -import '../models/yourLocation.dart'; abstract class YourLocationActions {} @@ -12,6 +13,7 @@ class AddYourLocationAction extends YourLocationActions { class DeleteYourLocationAction extends YourLocationActions { ObjectId id; + DeleteYourLocationAction(this.id); } diff --git a/lib/redux/yourLocationsReducer.dart b/lib/redux/yourLocationsReducer.dart index 1dfe505..03827fe 100644 --- a/lib/redux/yourLocationsReducer.dart +++ b/lib/redux/yourLocationsReducer.dart @@ -1,6 +1,6 @@ +import 'package:fires_flutter/models/yourLocation.dart'; import 'package:redux/redux.dart'; -import '../models/yourLocation.dart'; import 'actions.dart'; final yourLocationsReducer = combineReducers>([