From 2dfa745c6ac81bd9baef8b697705ba05753273fc Mon Sep 17 00:00:00 2001 From: vjrj Date: Wed, 27 Jun 2018 19:46:02 +0200 Subject: [PATCH] More redux work. User creation/retrieve --- lib/firebaseMessagingConf.dart | 36 +++++++++++----------- lib/firesApp.dart | 42 ++++++++++++++----------- lib/globals.dart | 1 + lib/mainCommon.dart | 32 ++++++++++--------- lib/mainDev.dart | 5 +-- lib/models/appState.dart | 36 ++++++++++++++-------- lib/models/appState.g.dart | 12 ++------ lib/models/firesApi.dart | 35 ++++++++++++++++++--- lib/models/user.dart | 29 ++++++++++++++++++ lib/redux/appActions.dart | 6 ++++ lib/redux/fetchDataMiddleware.dart | 49 ++++++++++++++++++++++-------- lib/redux/reducers.dart | 22 +++++++------- lib/redux/userIdReducer.dart | 3 -- lib/redux/userReducer.dart | 9 ++++++ lib/redux/userTokenReducer.dart | 3 -- pubspec.yaml | 2 +- 16 files changed, 212 insertions(+), 110 deletions(-) create mode 100644 lib/models/user.dart delete mode 100644 lib/redux/userIdReducer.dart create mode 100644 lib/redux/userReducer.dart delete mode 100644 lib/redux/userTokenReducer.dart diff --git a/lib/firebaseMessagingConf.dart b/lib/firebaseMessagingConf.dart index f6287ed..74c7ba6 100644 --- a/lib/firebaseMessagingConf.dart +++ b/lib/firebaseMessagingConf.dart @@ -1,26 +1,26 @@ import 'package:firebase_messaging/firebase_messaging.dart'; -import 'dart:async'; +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(); // https://pub.dartlang.org/packages/firebase_messaging#-example-tab- -void firebaseConfig() { - _firebaseMessaging.configure( - onLaunch: (Map message) { - print('On firebase launch'); - }, - onMessage: (Map message) { - print('On firebase message'); - }, - onResume: (Map message) { - print('On firebase resume'); - } - ); - getToken(); +void firebaseConfig(Store store) { + _firebaseMessaging.configure(onLaunch: (Map message) { + print('On firebase launch'); + }, onMessage: (Map message) { + print('On firebase message'); + }, onResume: (Map message) { + print('On firebase resume'); + }); + getToken(store); } -getToken() async { +getToken(Store store) async { String token = await _firebaseMessaging.onTokenRefresh.first; - // print(token); - // await _firebaseMessaging.getToken(); -} \ No newline at end of file + store.dispatch(new OnUserTokenAction(token)); +} diff --git a/lib/firesApp.dart b/lib/firesApp.dart index e514ff7..343433a 100644 --- a/lib/firesApp.dart +++ b/lib/firesApp.dart @@ -1,16 +1,17 @@ import 'package:comunes_flutter/comunes_flutter.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 'activeFires.dart'; import 'generated/i18n.dart'; import 'homePage.dart'; import 'introPage.dart'; +import 'models/appState.dart'; +import 'redux/actions.dart'; import 'sandbox.dart'; import 'theme.dart'; -import 'package:flutter_redux/flutter_redux.dart'; -import 'package:redux/redux.dart'; -import 'models/appState.dart'; class FiresApp extends StatelessWidget { static final WidgetBuilder introWidget = (context) => new IntroPage(); @@ -30,20 +31,25 @@ class FiresApp extends StatelessWidget { @override Widget build(BuildContext context) { - return new StoreProvider(store: this.store, child: - new MaterialApp( - localizationsDelegates: [ - S.delegate, - GlobalMaterialLocalizations.delegate, - GlobalWidgetsLocalizations.delegate, - ], - supportedLocales: S.delegate.supportedLocales, - localeResolutionCallback: - S.delegate.resolution(fallback: new Locale("en", "")), - home: new MaterialAppWithIntroHome( - introWidget, continueWidget, 'showInitialWizard-2018-06-27-01'), - onGenerateTitle: (context) => S.of(context).appName, - theme: firesTheme, - routes: routes)); + return new StoreProvider( + store: this.store, + child: new MaterialApp( + localizationsDelegates: [ + S.delegate, + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + ], + supportedLocales: S.delegate.supportedLocales, + localeResolutionCallback: + S.delegate.resolution(fallback: new Locale("en", "")), + home: new MaterialAppWithIntroHome( + introWidget, continueWidget, 'showInitialWizard-2018-06-27-01'), + onGenerateTitle: (context) { + String lang = Localizations.localeOf(context).languageCode; + this.store.dispatch(new OnUserLangAction(lang)); + return S.of(context).appName; + }, + theme: firesTheme, + routes: routes)); } } diff --git a/lib/globals.dart b/lib/globals.dart index 1091d6a..fe41b69 100644 --- a/lib/globals.dart +++ b/lib/globals.dart @@ -7,6 +7,7 @@ import 'dart:async'; import 'package:fires_flutter/models/yourLocation.dart'; +// FIXME remove this later String gmapKey; String firesApiKey; String firesApiUrl; diff --git a/lib/mainCommon.dart b/lib/mainCommon.dart index 0480087..0249d78 100644 --- a/lib/mainCommon.dart +++ b/lib/mainCommon.dart @@ -8,32 +8,34 @@ import 'firebaseMessagingConf.dart'; import 'firesApp.dart'; import 'globals.dart' as globals; import 'models/appState.dart'; +import 'models/firesApi.dart'; +import 'redux/fetchDataMiddleware.dart'; import 'redux/reducers.dart'; import 'yourLocationPersist.dart'; -import 'models/firesApi.dart'; - -import 'package:get_it/get_it.dart'; - +import 'package:redux_logging/redux_logging.dart'; Future> loadSecrets() async { return await SecretLoader(secretPath: 'assets/private-settings.json').load(); } void mainCommon(List> otherMiddleware) { - final store = new Store( - appStateReducer, - initialState: new AppState(), - middleware: otherMiddleware, - ); - globals.getIt.registerSingleton(new FiresApi()); - loadSecrets().then((secrets) { - globals.gmapKey = secrets['gmapKey']; - globals.firesApiKey = secrets['firesApiKey']; - globals.firesApiUrl = secrets['firesApiUrl'] + "api/v1/"; + + final store = new Store(appStateReducer, + initialState: new AppState( + gmapKey: secrets['gmapKey'], + firesApiKey: secrets['firesApiKey'], + firesApiUrl: secrets['firesApiUrl'] + "api/v1/"), + middleware: List.from(otherMiddleware)..add(fetchYourLocationsMiddleware)); + + // FIXME remove this later + globals.firesApiKey = store.state.firesApiKey; + globals.firesApiUrl = store.state.firesApiUrl; + globals.gmapKey = store.state.gmapKey; + globals.prefs.then((prefs) { loadYourLocationsWithPrefs(prefs); - firebaseConfig(); + firebaseConfig(store); // Run baby run! runApp(new FiresApp(store)); diff --git a/lib/mainDev.dart b/lib/mainDev.dart index c5c4ebc..1cbd849 100644 --- a/lib/mainDev.dart +++ b/lib/mainDev.dart @@ -2,12 +2,13 @@ import 'package:redux_logging/redux_logging.dart'; import 'globals.dart' as globals; import 'mainCommon.dart'; +import 'package:redux/redux.dart'; void main() { globals.isDevelopment = true; - List devMiddlewares = [ - new LoggingMiddleware(formatter: LoggingMiddleware.multiLineFormatter) + List devMiddlewares = [ + LoggingMiddleware.printer(formatter: LoggingMiddleware.multiLineFormatter) ]; mainCommon(devMiddlewares); diff --git a/lib/models/appState.dart b/lib/models/appState.dart index b46b3fd..0d052cd 100644 --- a/lib/models/appState.dart +++ b/lib/models/appState.dart @@ -1,9 +1,9 @@ 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 @@ -15,8 +15,14 @@ class AppState extends Object with _$AppStateSerializerMixin { final bool isLoaded; @JsonKey(ignore: true) final String error; - final String userId; - final String token; + @JsonKey(ignore: true) + final User user; + @JsonKey(ignore: true) + final String gmapKey; + @JsonKey(ignore: true) + final String firesApiKey; + @JsonKey(ignore: true) + final String firesApiUrl; final List yourLocations; @JsonKey(ignore: true) final FireMapState fireMapState; @@ -26,34 +32,40 @@ class AppState extends Object with _$AppStateSerializerMixin { _$AppStateFromJson(json); AppState( - {this.yourLocations = const [], - this.userId, - this.token, + {this.yourLocations : const [], + this.user: const User.initial(), this.isLoading: false, this.isLoaded: false, this.error: null, + this.gmapKey, + this.firesApiKey, + this.firesApiUrl, this.fireMapState: const FireMapState.initial()}); AppState copyWith( {bool isLoading, bool isLoaded, - String userId, - String token, + String user, String error, + String gmapKey, + String firesApiKey, + String firesApiUrl, List yourLocations, FireMapState fireMapState}) { return new AppState( isLoading: isLoading ?? this.isLoading, isLoaded: isLoaded ?? this.isLoaded, - userId: userId ?? this.userId, - token: token ?? this.token, + user: user ?? this.user, error: error ?? this.error, + gmapKey: gmapKey ?? this.gmapKey, + firesApiKey: firesApiKey ?? this.firesApiKey, + firesApiUrl: firesApiUrl ?? this.firesApiUrl, yourLocations: yourLocations ?? this.yourLocations, fireMapState: fireMapState ?? this.fireMapState); } @override String toString() { - return 'AppState{\nuserId: $userId\ntoken: $token\nisLoading: $isLoading\nisLoaded: $isLoaded\nYourLocations: $yourLocations}'; + return 'AppState{\nuser: ${user}\nisLoading: $isLoading\nisLoaded: $isLoaded\napiKey: ${ellipse(firesApiKey, 8)}\napiUrl: ${ellipse(firesApiUrl, 8)}'; } } diff --git a/lib/models/appState.g.dart b/lib/models/appState.g.dart index 8f4679d..8137ff8 100644 --- a/lib/models/appState.g.dart +++ b/lib/models/appState.g.dart @@ -11,13 +11,9 @@ part of 'appState.dart'; AppState _$AppStateFromJson(Map json) => new AppState( yourLocations: (json['yourLocations'] as List) .map((e) => new YourLocation.fromJson(e as Map)) - .toList(), - userId: json['userId'] as String, - token: json['token'] as String); + .toList()); abstract class _$AppStateSerializerMixin { - String get userId; - String get token; List get yourLocations; Map toJson() => new _$AppStateJsonMapWrapper(this); } @@ -27,16 +23,12 @@ class _$AppStateJsonMapWrapper extends $JsonMapWrapper { _$AppStateJsonMapWrapper(this._v); @override - Iterable get keys => const ['userId', 'token', 'yourLocations']; + Iterable get keys => const ['yourLocations']; @override dynamic operator [](Object key) { if (key is String) { switch (key) { - case 'userId': - return _v.userId; - case 'token': - return _v.token; case 'yourLocations': return _v.yourLocations; } diff --git a/lib/models/firesApi.dart b/lib/models/firesApi.dart index 1662c4c..f746249 100644 --- a/lib/models/firesApi.dart +++ b/lib/models/firesApi.dart @@ -1,14 +1,39 @@ -import 'yourLocation.dart'; import 'dart:async'; +import 'dart:convert'; + +import 'package:http/http.dart' as ht; import 'package:jaguar_resty/jaguar_resty.dart' as resty; +import 'appState.dart'; +import 'yourLocation.dart'; + class FiresApi { - - Future createUser(String token) async { - + FiresApi() { + resty.globalClient = new ht.IOClient(); } - Future> fetchYourLocations() async { + Future createUser(AppState state, String token, String lang) async { + assert(state.firesApiUrl != null); + assert(state.firesApiKey != null); + assert(token != null); + assert(lang != null); + final params = { + "token": state.firesApiKey, + "mobileToken": token, + "lang": lang + }; + final url = '${state.firesApiUrl}mobile/users'; + /* print(url); + print(params); */ + String resp = await resty.post(url).json(params).go().then((response) { + if (response.statusCode == 200) { + // print(response.body); + return json.decode(response.body)['data']['userId']; + } + }); + return resp; } + + Future> fetchYourLocations() async {} } diff --git a/lib/models/user.dart b/lib/models/user.dart new file mode 100644 index 0000000..364acde --- /dev/null +++ b/lib/models/user.dart @@ -0,0 +1,29 @@ +import 'package:meta/meta.dart'; +import 'package:comunes_flutter/comunes_flutter.dart'; + +@immutable +class User { + final String userId; + final String lang; + final String token; + + const User.initial() + : this.userId = null, + lang = null, + token = null; + + User({this.userId, this.lang, this.token}); + + User copyWith({String userId, String lang, String token}) { + return new User( + userId: userId ?? this.userId, + token: token ?? this.token, + lang: lang ?? this.lang); + } + + @override + String toString() { + return 'User {userId: $userId, lang: $lang, token: ${ellipse(token)}'; + } + +} diff --git a/lib/redux/appActions.dart b/lib/redux/appActions.dart index 8db77da..e0709ad 100644 --- a/lib/redux/appActions.dart +++ b/lib/redux/appActions.dart @@ -26,4 +26,10 @@ class OnUserCreatedAction extends AppActions { final String userId; OnUserCreatedAction(this.userId); +} + +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 a0afc0b..8787fa6 100644 --- a/lib/redux/fetchDataMiddleware.dart +++ b/lib/redux/fetchDataMiddleware.dart @@ -1,22 +1,39 @@ import 'package:redux/redux.dart'; -import 'actions.dart'; -import '../models/yourLocation.dart'; -import '../models/appState.dart'; + import '../globals.dart' as globals; +import '../models/appState.dart'; import '../models/firesApi.dart'; +import '../models/yourLocation.dart'; +import 'actions.dart'; // A middleware takes in 3 parameters: your Store, which you can use to -// read state or dispatch new actions, the action that was dispatched, -// and a `next` function. The first two you know about, and the `next` -// function is responsible for sending the action to your Reducer, or +// read state or dispatch new actions, the action that was dispatched, +// and a `next` function. The first two you know about, and the `next` +// function is responsible for sending the action to your Reducer, or // the next Middleware if you provide more than one. // // Middleware do not return any values themselves. They simply forward // actions on to the Reducer or swallow actions in some special cases. -void fetchYourLocationsMiddleware(Store store, action, NextDispatcher next) { - // If our Middleware encounters a `FetchYourLocationAction` - if (action is FetchYourLocationsAction) { +FiresApi api = globals.getIt.get(); + +void fetchYourLocationsMiddleware( + Store store, action, NextDispatcher next) { + // If our Middleware encounters a `FetchYourLocationAction` + + if (action is OnUserLangAction) { + // I can create the user with the lang and the token + if (store.state.user.token != null) + createUser(store, action.lang, store.state.user.token); + } + + if (action is OnUserTokenAction) { + // I can create the user with the lang and the token + if (store.state.user.lang != null) + createUser(store, store.state.user.lang, action.token); + } + + if (action is FetchYourLocationsAction) { final api = globals.getIt.get(); // Use the api to fetch the YourLocations @@ -28,9 +45,17 @@ void fetchYourLocationsMiddleware(Store store, action, NextDispatcher // If it fails, dispatch a failure action. The reducer will // update the state with the error. store.dispatch(new FetchYourLocationsFailedAction(error)); - }); + }); } - + // Make sure our actions continue on to the reducer. next(action); -} \ No newline at end of file +} + +void createUser(store, lang, token) { + assert(token != null, "User lang is null"); + assert(token != null, "User mobile token is null"); + api + .createUser(store.state, token, lang) + .then((userId) => store.dispatch(new OnUserCreatedAction(userId))); +} diff --git a/lib/redux/reducers.dart b/lib/redux/reducers.dart index 74bd530..3d07354 100644 --- a/lib/redux/reducers.dart +++ b/lib/redux/reducers.dart @@ -1,22 +1,22 @@ import '../models/appState.dart'; -import 'yourLocationsReducer.dart'; +import 'errorReducer.dart'; +import 'fireMapReducer.dart'; import 'loadedReducer.dart'; import 'loadingReducer.dart'; -import 'errorReducer.dart'; -import 'userIdReducer.dart'; -import 'userTokenReducer.dart'; -import 'fireMapReducer.dart'; +import 'userReducer.dart'; +import 'yourLocationsReducer.dart'; // We create the State reducer by combining many smaller reducers into one! AppState appStateReducer(AppState state, action) { - - return AppState( + return AppState( yourLocations: yourLocationsReducer(state.yourLocations, action), - userId: userReducer(state.userId, action), - token: userTokenReducer(state.token, action), + 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) - ); + fireMapState: fireMapReducer(state.fireMapState, action), + firesApiKey: state.firesApiKey, + firesApiUrl: state.firesApiUrl, + gmapKey: state.gmapKey + ); } diff --git a/lib/redux/userIdReducer.dart b/lib/redux/userIdReducer.dart deleted file mode 100644 index 3108d0d..0000000 --- a/lib/redux/userIdReducer.dart +++ /dev/null @@ -1,3 +0,0 @@ -String userReducer(String userId, action) { - return userId; -} \ No newline at end of file diff --git a/lib/redux/userReducer.dart b/lib/redux/userReducer.dart new file mode 100644 index 0000000..61c2b3a --- /dev/null +++ b/lib/redux/userReducer.dart @@ -0,0 +1,9 @@ +import 'actions.dart'; +import '../models/user.dart'; + +User userReducer(User user, action) { + if (action is OnUserCreatedAction) return user.copyWith(userId: action.userId); + if (action is OnUserTokenAction) return user.copyWith(token: action.token); + if (action is OnUserLangAction) return user.copyWith(lang: action.lang); + return user; +} diff --git a/lib/redux/userTokenReducer.dart b/lib/redux/userTokenReducer.dart deleted file mode 100644 index 5aee7c3..0000000 --- a/lib/redux/userTokenReducer.dart +++ /dev/null @@ -1,3 +0,0 @@ -String userTokenReducer(String userToken, action) { - return userToken; -} \ No newline at end of file diff --git a/pubspec.yaml b/pubspec.yaml index ed828c4..ff9177d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -25,7 +25,7 @@ dependencies: bson_objectid: "^0.1.0" comunes_flutter: path: /home/vjrj/dev/comunes_flutter - version: "^0.0.10" + version: "^0.0.12" # redux redux: "^3.0.0"