Working on login
This commit is contained in:
parent
9487c89307
commit
b444ce2e2a
|
|
@ -16,7 +16,7 @@ class FileStorage {
|
||||||
);
|
);
|
||||||
|
|
||||||
/// LoadProducts
|
/// LoadProducts
|
||||||
Future<List<ProductEntity>> loadProducts() async {
|
Future<List<dynamic>> loadData() async {
|
||||||
final file = await _getLocalFile();
|
final file = await _getLocalFile();
|
||||||
final contents = await file.readAsString();
|
final contents = await file.readAsString();
|
||||||
|
|
||||||
|
|
@ -29,7 +29,7 @@ class FileStorage {
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<File> saveProducts(List<ProductEntity> products) async {
|
Future<File> saveData(List<dynamic> products) async {
|
||||||
final file = await _getLocalFile();
|
final file = await _getLocalFile();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
||||||
|
|
@ -3,18 +3,18 @@ import 'package:json_annotation/json_annotation.dart';
|
||||||
part 'entities.g.dart';
|
part 'entities.g.dart';
|
||||||
|
|
||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
class ProductResponse extends Object with _$ProductResponseSerializerMixin {
|
class BaseResponse extends Object with _$BaseResponseSerializerMixin {
|
||||||
//final String message;
|
//final String message;
|
||||||
|
|
||||||
@JsonKey(name: "data")
|
@JsonKey(name: "data")
|
||||||
final List<ProductEntity> products;
|
final List<dynamic> data;
|
||||||
|
|
||||||
ProductResponse(
|
BaseResponse(
|
||||||
//this.message,
|
//this.message,
|
||||||
this.products,
|
this.data,
|
||||||
);
|
);
|
||||||
|
|
||||||
factory ProductResponse.fromJson(Map<String, dynamic> json) => _$ProductResponseFromJson(json);
|
factory BaseResponse.fromJson(Map<String, dynamic> json) => _$BaseResponseFromJson(json);
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
|
|
|
||||||
|
|
@ -6,16 +6,12 @@ part of 'entities.dart';
|
||||||
// Generator: JsonSerializableGenerator
|
// Generator: JsonSerializableGenerator
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
ProductResponse _$ProductResponseFromJson(Map<String, dynamic> json) =>
|
BaseResponse _$BaseResponseFromJson(Map<String, dynamic> json) =>
|
||||||
new ProductResponse((json['data'] as List)
|
new BaseResponse(json['data'] as List);
|
||||||
?.map((e) => e == null
|
|
||||||
? null
|
|
||||||
: new ProductEntity.fromJson(e as Map<String, dynamic>))
|
|
||||||
?.toList());
|
|
||||||
|
|
||||||
abstract class _$ProductResponseSerializerMixin {
|
abstract class _$BaseResponseSerializerMixin {
|
||||||
List<ProductEntity> get products;
|
List<dynamic> get data;
|
||||||
Map<String, dynamic> toJson() => <String, dynamic>{'data': products};
|
Map<String, dynamic> toJson() => <String, dynamic>{'data': data};
|
||||||
}
|
}
|
||||||
|
|
||||||
ProductEntity _$ProductEntityFromJson(Map<String, dynamic> json) =>
|
ProductEntity _$ProductEntityFromJson(Map<String, dynamic> json) =>
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ export 'package:invoiceninja/data/models/entities.dart';
|
||||||
|
|
||||||
class User {
|
class User {
|
||||||
final String token;
|
final String token;
|
||||||
final String id;
|
final int id;
|
||||||
|
|
||||||
User(this.token, this.id);
|
User(this.token, this.id);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import 'dart:async';
|
||||||
import 'dart:core';
|
import 'dart:core';
|
||||||
import 'package:meta/meta.dart';
|
import 'package:meta/meta.dart';
|
||||||
|
|
||||||
|
import 'package:invoiceninja/redux/auth/auth_state.dart';
|
||||||
import 'package:invoiceninja/data/models/entities.dart';
|
import 'package:invoiceninja/data/models/entities.dart';
|
||||||
import 'package:invoiceninja/data/repositories/repositories.dart';
|
import 'package:invoiceninja/data/repositories/repositories.dart';
|
||||||
import 'package:invoiceninja/data/file_storage.dart';
|
import 'package:invoiceninja/data/file_storage.dart';
|
||||||
|
|
@ -9,7 +10,7 @@ import 'package:invoiceninja/data/web_client.dart';
|
||||||
|
|
||||||
/// A class that glues together our local file storage and web client. It has a
|
/// A class that glues together our local file storage and web client. It has a
|
||||||
/// clear responsibility: Load Products and Persist products.
|
/// clear responsibility: Load Products and Persist products.
|
||||||
class ProductsRepositoryFlutter implements ProductsRepository {
|
class ProductsRepositoryFlutter implements BaseRepository {
|
||||||
final FileStorage fileStorage;
|
final FileStorage fileStorage;
|
||||||
final WebClient webClient;
|
final WebClient webClient;
|
||||||
|
|
||||||
|
|
@ -21,29 +22,35 @@ class ProductsRepositoryFlutter implements ProductsRepository {
|
||||||
/// Loads products first from File storage. If they don't exist or encounter an
|
/// Loads products first from File storage. If they don't exist or encounter an
|
||||||
/// error, it attempts to load the Products from a Web Client.
|
/// error, it attempts to load the Products from a Web Client.
|
||||||
@override
|
@override
|
||||||
Future<List<ProductEntity>> loadProducts() async {
|
Future<List<dynamic>> loadData(AuthState auth) async {
|
||||||
|
|
||||||
print('ProductRepo: loadProducts...');
|
print('ProductRepo: loadProducts...');
|
||||||
|
|
||||||
|
final products = await webClient.fetchData(
|
||||||
|
auth.url + '/products', auth.token);
|
||||||
|
|
||||||
|
//fileStorage.saveProducts(products);
|
||||||
|
|
||||||
|
return products.map((product) => ProductEntity.fromJson(product)).toList();
|
||||||
|
|
||||||
|
/*
|
||||||
try {
|
try {
|
||||||
return await fileStorage.loadProducts();
|
return await fileStorage.loadData();
|
||||||
} catch (e) {
|
} catch (exception) {
|
||||||
final products = await webClient.fetchProducts();
|
final products = await webClient.fetchData(
|
||||||
|
auth.url + '/products', auth.token);
|
||||||
|
|
||||||
print('ProductRepo: result');
|
//fileStorage.saveProducts(products);
|
||||||
print(products);
|
|
||||||
|
|
||||||
fileStorage.saveProducts(products);
|
|
||||||
|
|
||||||
return products;
|
return products;
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
// Persists products to local disk and the web
|
// Persists products to local disk and the web
|
||||||
@override
|
@override
|
||||||
Future saveProducts(List<ProductEntity> products) {
|
Future saveData(List<dynamic> products) {
|
||||||
return Future.wait<dynamic>([
|
return Future.wait<dynamic>([
|
||||||
fileStorage.saveProducts(products),
|
fileStorage.saveData(products),
|
||||||
webClient.postProducts(products),
|
webClient.postProducts(products),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:core';
|
import 'dart:core';
|
||||||
|
import 'package:invoiceninja/redux/auth/auth_state.dart';
|
||||||
|
|
||||||
import 'package:invoiceninja/data/models/entities.dart';
|
import 'package:invoiceninja/data/models/entities.dart';
|
||||||
|
|
||||||
|
|
@ -11,11 +12,11 @@ import 'package:invoiceninja/data/models/entities.dart';
|
||||||
/// The domain layer should depend on this abstract class, and each app can
|
/// The domain layer should depend on this abstract class, and each app can
|
||||||
/// inject the correct implementation depending on the environment, such as
|
/// inject the correct implementation depending on the environment, such as
|
||||||
/// web or Flutter.
|
/// web or Flutter.
|
||||||
abstract class ProductsRepository {
|
abstract class BaseRepository {
|
||||||
/// Loads products first from File storage. If they don't exist or encounter an
|
/// Loads products first from File storage. If they don't exist or encounter an
|
||||||
/// error, it attempts to load the Todos from a Web Client.
|
/// error, it attempts to load the Todos from a Web Client.
|
||||||
Future<List<ProductEntity>> loadProducts();
|
Future<List<dynamic>> loadData(AuthState auth);
|
||||||
|
|
||||||
// Persists products to local disk and the web
|
// Persists products to local disk and the web
|
||||||
Future saveProducts(List<ProductEntity> products);
|
Future saveData(List<dynamic> data);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,20 +13,16 @@ class WebClient {
|
||||||
const WebClient();
|
const WebClient();
|
||||||
|
|
||||||
/// Mock that "fetches" some Products from a "web service" after a short delay
|
/// Mock that "fetches" some Products from a "web service" after a short delay
|
||||||
Future<List<ProductEntity>> fetchProducts() async {
|
Future<List<dynamic>> fetchData(String url, String token) async {
|
||||||
|
|
||||||
print('Web Client: fetchProducts...');
|
|
||||||
|
|
||||||
var url = "";
|
|
||||||
|
|
||||||
final response = await http.Client().get(
|
final response = await http.Client().get(
|
||||||
url,
|
url,
|
||||||
headers: {'X-Ninja-Token': ""},
|
headers: {'X-Ninja-Token': token},
|
||||||
);
|
);
|
||||||
|
|
||||||
return ProductResponse
|
return BaseResponse
|
||||||
.fromJson(json.decode(response.body))
|
.fromJson(json.decode(response.body))
|
||||||
.products
|
.data
|
||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import 'package:invoiceninja/routes.dart';
|
||||||
import 'package:invoiceninja/redux/product/product_actions.dart';
|
import 'package:invoiceninja/redux/product/product_actions.dart';
|
||||||
import 'package:invoiceninja/redux/product/product_middleware.dart';
|
import 'package:invoiceninja/redux/product/product_middleware.dart';
|
||||||
import 'package:invoiceninja/redux/app/app_reducer.dart';
|
import 'package:invoiceninja/redux/app/app_reducer.dart';
|
||||||
|
import 'package:redux_logging/redux_logging.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
|
|
||||||
|
|
@ -25,7 +26,11 @@ class InvoiceNinjaApp extends StatelessWidget {
|
||||||
final store = Store<AppState>(
|
final store = Store<AppState>(
|
||||||
appReducer,
|
appReducer,
|
||||||
initialState: AppState.loading(),
|
initialState: AppState.loading(),
|
||||||
middleware: createStoreProductsMiddleware(),
|
middleware: []
|
||||||
|
..addAll(createStoreProductsMiddleware())
|
||||||
|
..addAll([
|
||||||
|
LoggingMiddleware.printer(),
|
||||||
|
])
|
||||||
);
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
@ -40,7 +45,6 @@ class InvoiceNinjaApp extends StatelessWidget {
|
||||||
theme: new ThemeData.dark(),
|
theme: new ThemeData.dark(),
|
||||||
title: 'Invoice Ninja',
|
title: 'Invoice Ninja',
|
||||||
routes: {
|
routes: {
|
||||||
/*
|
|
||||||
NinjaRoutes.login: (context) {
|
NinjaRoutes.login: (context) {
|
||||||
return StoreBuilder<AppState>(
|
return StoreBuilder<AppState>(
|
||||||
builder: (context, store) {
|
builder: (context, store) {
|
||||||
|
|
@ -48,7 +52,6 @@ class InvoiceNinjaApp extends StatelessWidget {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
*/
|
|
||||||
NinjaRoutes.dashboard: (context) {
|
NinjaRoutes.dashboard: (context) {
|
||||||
return StoreBuilder<AppState>(
|
return StoreBuilder<AppState>(
|
||||||
builder: (context, store) {
|
builder: (context, store) {
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ AppState appReducer(AppState state, action) {
|
||||||
|
|
||||||
return AppState(
|
return AppState(
|
||||||
isLoading: loadingReducer(state.isLoading, action),
|
isLoading: loadingReducer(state.isLoading, action),
|
||||||
auth: authReducer(state.auth, action),
|
|
||||||
products: productsReducer(state.products, action),
|
products: productsReducer(state.products, action),
|
||||||
|
auth: authReducer(state.auth, action),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,9 @@ class AppState {
|
||||||
|
|
||||||
AppState(
|
AppState(
|
||||||
{this.isLoading = false,
|
{this.isLoading = false,
|
||||||
AuthState auth,
|
this.products = const [],
|
||||||
this.products = const []});
|
AuthState auth}):
|
||||||
|
auth = auth ?? new AuthState();
|
||||||
|
|
||||||
|
|
||||||
factory AppState.loading() => AppState(isLoading: true);
|
factory AppState.loading() => AppState(isLoading: true);
|
||||||
|
|
@ -55,6 +56,6 @@ class AppState {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'AppState{isLoading: $isLoading}';
|
return 'AppState{isLoading: $isLoading, url: ${auth.url}, token ${auth.token}}';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -3,7 +3,16 @@ import 'package:redux/redux.dart';
|
||||||
import 'package:invoiceninja/redux/app/app_state.dart';
|
import 'package:invoiceninja/redux/app/app_state.dart';
|
||||||
import 'package:invoiceninja/data/models/models.dart';
|
import 'package:invoiceninja/data/models/models.dart';
|
||||||
|
|
||||||
class UserLoginRequest {}
|
class UserLoginRequest {
|
||||||
|
final String email;
|
||||||
|
final String password;
|
||||||
|
final String url;
|
||||||
|
final String secret;
|
||||||
|
final String token;
|
||||||
|
|
||||||
|
//UserLoginRequest(this.email, this.password, this.url, this.secret);
|
||||||
|
UserLoginRequest(this.url, this.token);
|
||||||
|
}
|
||||||
|
|
||||||
class UserLoginSuccess {
|
class UserLoginSuccess {
|
||||||
final User user;
|
final User user;
|
||||||
|
|
@ -19,21 +28,21 @@ class UserLoginFailure {
|
||||||
|
|
||||||
class UserLogout {}
|
class UserLogout {}
|
||||||
|
|
||||||
final Function login = (BuildContext context, String username, String password) {
|
/*
|
||||||
|
final Function login = (BuildContext context, String url, String token) {
|
||||||
return (Store<AppState> store) {
|
return (Store<AppState> store) {
|
||||||
store.dispatch(new UserLoginRequest());
|
store.dispatch(new UserLoginRequest(url, token));
|
||||||
if (username == 'asd' && password == 'asd') {
|
store.dispatch(new UserLoginSuccess(new User(token, 1)));
|
||||||
store.dispatch(new UserLoginSuccess(new User('placeholder_token', 'placeholder_id')));
|
Navigator.of(context).pushNamed('/dashboard');
|
||||||
Navigator.of(context).pushNamedAndRemoveUntil('/main', (_) => false);
|
|
||||||
} else {
|
|
||||||
store.dispatch(new UserLoginFailure('Username or password were incorrect.'));
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
final Function logout = (BuildContext context) {
|
final Function logout = (BuildContext context) {
|
||||||
return (Store<AppState> store) {
|
return (Store<AppState> store) {
|
||||||
store.dispatch(new UserLogout());
|
store.dispatch(new UserLogout());
|
||||||
Navigator.of(context).pushNamedAndRemoveUntil('/login', (_) => false);
|
Navigator.of(context).pushNamedAndRemoveUntil('/login', (_) => false);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
*/
|
||||||
|
|
@ -4,21 +4,24 @@ import 'package:invoiceninja/redux/auth/auth_actions.dart';
|
||||||
import 'package:invoiceninja/redux/auth/auth_state.dart';
|
import 'package:invoiceninja/redux/auth/auth_state.dart';
|
||||||
|
|
||||||
Reducer<AuthState> authReducer = combineReducers([
|
Reducer<AuthState> authReducer = combineReducers([
|
||||||
new TypedReducer<AuthState, UserLoginRequest>(userLoginRequestReducer),
|
TypedReducer<AuthState, UserLoginRequest>(userLoginRequestReducer),
|
||||||
new TypedReducer<AuthState, UserLoginSuccess>(userLoginSuccessReducer),
|
TypedReducer<AuthState, UserLoginSuccess>(userLoginSuccessReducer),
|
||||||
new TypedReducer<AuthState, UserLoginFailure>(userLoginFailureReducer),
|
TypedReducer<AuthState, UserLoginFailure>(userLoginFailureReducer),
|
||||||
new TypedReducer<AuthState, UserLogout>(userLogoutReducer),
|
TypedReducer<AuthState, UserLogout>(userLogoutReducer),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
AuthState userLoginRequestReducer(AuthState auth, UserLoginRequest action) {
|
AuthState userLoginRequestReducer(AuthState auth, UserLoginRequest action) {
|
||||||
return new AuthState().copyWith(
|
return AuthState().copyWith(
|
||||||
|
url: action.url,
|
||||||
|
token: action.token,
|
||||||
|
secret: action.secret,
|
||||||
isAuthenticated: false,
|
isAuthenticated: false,
|
||||||
isAuthenticating: true,
|
isAuthenticating: true,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
AuthState userLoginSuccessReducer(AuthState auth, UserLoginSuccess action) {
|
AuthState userLoginSuccessReducer(AuthState auth, UserLoginSuccess action) {
|
||||||
return new AuthState().copyWith(
|
return AuthState().copyWith(
|
||||||
isAuthenticated: true,
|
isAuthenticated: true,
|
||||||
isAuthenticating: false,
|
isAuthenticating: false,
|
||||||
user: action.user
|
user: action.user
|
||||||
|
|
@ -26,7 +29,7 @@ AuthState userLoginSuccessReducer(AuthState auth, UserLoginSuccess action) {
|
||||||
}
|
}
|
||||||
|
|
||||||
AuthState userLoginFailureReducer(AuthState auth, UserLoginFailure action) {
|
AuthState userLoginFailureReducer(AuthState auth, UserLoginFailure action) {
|
||||||
return new AuthState().copyWith(
|
return AuthState().copyWith(
|
||||||
isAuthenticated: false,
|
isAuthenticated: false,
|
||||||
isAuthenticating: false,
|
isAuthenticating: false,
|
||||||
error: action.error
|
error: action.error
|
||||||
|
|
@ -34,5 +37,5 @@ AuthState userLoginFailureReducer(AuthState auth, UserLoginFailure action) {
|
||||||
}
|
}
|
||||||
|
|
||||||
AuthState userLogoutReducer(AuthState auth, UserLogout action) {
|
AuthState userLogoutReducer(AuthState auth, UserLogout action) {
|
||||||
return new AuthState();
|
return AuthState();
|
||||||
}
|
}
|
||||||
|
|
@ -6,6 +6,9 @@ import 'package:invoiceninja/data/models/models.dart';
|
||||||
class AuthState {
|
class AuthState {
|
||||||
|
|
||||||
// properties
|
// properties
|
||||||
|
final String url;
|
||||||
|
final String secret;
|
||||||
|
final String token;
|
||||||
final bool isAuthenticated;
|
final bool isAuthenticated;
|
||||||
final bool isAuthenticating;
|
final bool isAuthenticating;
|
||||||
final User user;
|
final User user;
|
||||||
|
|
@ -13,6 +16,9 @@ class AuthState {
|
||||||
|
|
||||||
// constructor with default
|
// constructor with default
|
||||||
AuthState({
|
AuthState({
|
||||||
|
this.url,
|
||||||
|
this.secret,
|
||||||
|
this.token,
|
||||||
this.isAuthenticated = false,
|
this.isAuthenticated = false,
|
||||||
this.isAuthenticating = false,
|
this.isAuthenticating = false,
|
||||||
this.user,
|
this.user,
|
||||||
|
|
@ -21,12 +27,18 @@ class AuthState {
|
||||||
|
|
||||||
// allows to modify AuthState parameters while cloning previous ones
|
// allows to modify AuthState parameters while cloning previous ones
|
||||||
AuthState copyWith({
|
AuthState copyWith({
|
||||||
|
String url,
|
||||||
|
String secret,
|
||||||
|
String token,
|
||||||
bool isAuthenticated,
|
bool isAuthenticated,
|
||||||
bool isAuthenticating,
|
bool isAuthenticating,
|
||||||
String error,
|
String error,
|
||||||
User user
|
User user
|
||||||
}) {
|
}) {
|
||||||
return new AuthState(
|
return new AuthState(
|
||||||
|
url: url ?? this.url,
|
||||||
|
token: token ?? this.token,
|
||||||
|
secret: secret ?? this.secret,
|
||||||
isAuthenticated: isAuthenticated ?? this.isAuthenticated,
|
isAuthenticated: isAuthenticated ?? this.isAuthenticated,
|
||||||
isAuthenticating: isAuthenticating ?? this.isAuthenticating,
|
isAuthenticating: isAuthenticating ?? this.isAuthenticating,
|
||||||
error: error ?? this.error,
|
error: error ?? this.error,
|
||||||
|
|
@ -35,6 +47,9 @@ class AuthState {
|
||||||
}
|
}
|
||||||
|
|
||||||
factory AuthState.fromJSON(Map<String, dynamic> json) => new AuthState(
|
factory AuthState.fromJSON(Map<String, dynamic> json) => new AuthState(
|
||||||
|
url: json['url'],
|
||||||
|
token: json['token'],
|
||||||
|
secret: json['secret'],
|
||||||
isAuthenticated: json['isAuthenticated'],
|
isAuthenticated: json['isAuthenticated'],
|
||||||
isAuthenticating: json['isAuthenticating'],
|
isAuthenticating: json['isAuthenticating'],
|
||||||
error: json['error'],
|
error: json['error'],
|
||||||
|
|
@ -42,6 +57,9 @@ class AuthState {
|
||||||
);
|
);
|
||||||
|
|
||||||
Map<String, dynamic> toJSON() => <String, dynamic>{
|
Map<String, dynamic> toJSON() => <String, dynamic>{
|
||||||
|
'url': this.url,
|
||||||
|
'token': this.token,
|
||||||
|
'secret': this.secret,
|
||||||
'isAuthenticated': this.isAuthenticated,
|
'isAuthenticated': this.isAuthenticated,
|
||||||
'isAuthenticating': this.isAuthenticating,
|
'isAuthenticating': this.isAuthenticating,
|
||||||
'user': this.user == null ? null : this.user.toJSON(),
|
'user': this.user == null ? null : this.user.toJSON(),
|
||||||
|
|
@ -51,6 +69,7 @@ class AuthState {
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return '''{
|
return '''{
|
||||||
|
url: $url,
|
||||||
isAuthenticated: $isAuthenticated,
|
isAuthenticated: $isAuthenticated,
|
||||||
isAuthenticating: $isAuthenticating,
|
isAuthenticating: $isAuthenticating,
|
||||||
user: $user,
|
user: $user,
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,16 @@ import 'package:invoiceninja/data/models/models.dart';
|
||||||
|
|
||||||
class LoadProductsAction {}
|
class LoadProductsAction {}
|
||||||
|
|
||||||
class ProductsNotLoadedAction {}
|
class ProductsNotLoadedAction {
|
||||||
|
final dynamic error;
|
||||||
|
|
||||||
|
ProductsNotLoadedAction(this.error);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'ProductsNotLoadedAction{products: $error}';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class ProductsLoadedAction {
|
class ProductsLoadedAction {
|
||||||
final List<ProductEntity> products;
|
final List<ProductEntity> products;
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,10 @@ import 'package:invoiceninja/data/repositories/repositories.dart';
|
||||||
import 'package:invoiceninja/data/repositories/product_repository.dart';
|
import 'package:invoiceninja/data/repositories/product_repository.dart';
|
||||||
import 'package:invoiceninja/data/file_storage.dart';
|
import 'package:invoiceninja/data/file_storage.dart';
|
||||||
import 'package:invoiceninja/redux/product/product_selectors.dart';
|
import 'package:invoiceninja/redux/product/product_selectors.dart';
|
||||||
|
import 'package:invoiceninja/data/models/entities.dart';
|
||||||
|
|
||||||
List<Middleware<AppState>> createStoreProductsMiddleware([
|
List<Middleware<AppState>> createStoreProductsMiddleware([
|
||||||
ProductsRepository repository = const ProductsRepositoryFlutter(
|
BaseRepository repository = const ProductsRepositoryFlutter(
|
||||||
fileStorage: const FileStorage(
|
fileStorage: const FileStorage(
|
||||||
'__invoiceninja__',
|
'__invoiceninja__',
|
||||||
getApplicationDocumentsDirectory,
|
getApplicationDocumentsDirectory,
|
||||||
|
|
@ -24,7 +25,7 @@ List<Middleware<AppState>> createStoreProductsMiddleware([
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
Middleware<AppState> _createSaveProducts(ProductsRepository repository) {
|
Middleware<AppState> _createSaveProducts(BaseRepository repository) {
|
||||||
return (Store<AppState> store, action, NextDispatcher next) {
|
return (Store<AppState> store, action, NextDispatcher next) {
|
||||||
next(action);
|
next(action);
|
||||||
|
|
||||||
|
|
@ -36,14 +37,12 @@ Middleware<AppState> _createSaveProducts(ProductsRepository repository) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
Middleware<AppState> _createLoadProducts(ProductsRepository repository) {
|
Middleware<AppState> _createLoadProducts(BaseRepository repository) {
|
||||||
return (Store<AppState> store, action, NextDispatcher next) {
|
return (Store<AppState> store, action, NextDispatcher next) {
|
||||||
|
|
||||||
print('Product Middleware: createLoadProducts...');
|
print('Product Middleware: createLoadProducts...');
|
||||||
|
|
||||||
//repository.loadProducts();
|
repository.loadData(store.state.auth).then(
|
||||||
|
|
||||||
repository.loadProducts().then(
|
|
||||||
(products) {
|
(products) {
|
||||||
store.dispatch(
|
store.dispatch(
|
||||||
ProductsLoadedAction(
|
ProductsLoadedAction(
|
||||||
|
|
@ -52,7 +51,7 @@ Middleware<AppState> _createLoadProducts(ProductsRepository repository) {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
).catchError((_) => store.dispatch(ProductsNotLoadedAction()));
|
).catchError((error) => store.dispatch(ProductsNotLoadedAction(error)));
|
||||||
|
|
||||||
next(action);
|
next(action);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
class NinjaRoutes {
|
class NinjaRoutes {
|
||||||
//static final login = "/";
|
static final login = "/";
|
||||||
static final dashboard = "/";
|
static final dashboard = "/dashboard";
|
||||||
static final clientList = "/clientList";
|
static final clientList = "/clientList";
|
||||||
static final productList = "/productList";
|
static final productList = "/productList";
|
||||||
}
|
}
|
||||||
|
|
@ -3,6 +3,7 @@ import 'package:redux/redux.dart';
|
||||||
import 'package:flutter_redux/flutter_redux.dart';
|
import 'package:flutter_redux/flutter_redux.dart';
|
||||||
import 'package:invoiceninja/redux/app/app_state.dart';
|
import 'package:invoiceninja/redux/app/app_state.dart';
|
||||||
import 'package:invoiceninja/redux/auth/auth_actions.dart';
|
import 'package:invoiceninja/redux/auth/auth_actions.dart';
|
||||||
|
import 'package:invoiceninja/data/models/models.dart';
|
||||||
|
|
||||||
class LoginScreen extends StatefulWidget {
|
class LoginScreen extends StatefulWidget {
|
||||||
@override
|
@override
|
||||||
|
|
@ -14,6 +15,9 @@ class _LoginScreenState extends State<LoginScreen> {
|
||||||
|
|
||||||
String _username;
|
String _username;
|
||||||
String _password;
|
String _password;
|
||||||
|
String _url;
|
||||||
|
String _token;
|
||||||
|
String _secret;
|
||||||
|
|
||||||
void _submit() {
|
void _submit() {
|
||||||
final form = _formKey.currentState;
|
final form = _formKey.currentState;
|
||||||
|
|
@ -27,8 +31,11 @@ class _LoginScreenState extends State<LoginScreen> {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return StoreConnector<AppState, dynamic>(
|
return StoreConnector<AppState, dynamic>(
|
||||||
converter: (Store<AppState> store) {
|
converter: (Store<AppState> store) {
|
||||||
return (BuildContext context, String username, String password) =>
|
return (BuildContext context, String url, String token) {
|
||||||
store.dispatch(login(context, username, password));
|
store.dispatch(UserLoginRequest(url, token));
|
||||||
|
//store.dispatch(UserLoginSuccess(User(token, 1)));
|
||||||
|
Navigator.of(context).pushNamed('/dashboard');
|
||||||
|
};
|
||||||
}, builder: (BuildContext context, loginAction) {
|
}, builder: (BuildContext context, loginAction) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
body: Form(
|
body: Form(
|
||||||
|
|
@ -37,26 +44,41 @@ class _LoginScreenState extends State<LoginScreen> {
|
||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
padding: EdgeInsets.only(left: 24.0, right: 24.0, top: 40.0),
|
padding: EdgeInsets.only(left: 24.0, right: 24.0, top: 40.0),
|
||||||
children: [
|
children: [
|
||||||
|
TextFormField(
|
||||||
|
decoration: InputDecoration(labelText: 'URL'),
|
||||||
|
validator: (val) =>
|
||||||
|
val.isEmpty ? 'Please enter your URL.' : null,
|
||||||
|
onSaved: (val) => _url = val,
|
||||||
|
),
|
||||||
|
TextFormField(
|
||||||
|
decoration: InputDecoration(labelText: 'Token'),
|
||||||
|
validator: (val) =>
|
||||||
|
val.isEmpty ? 'Please enter your token.' : null,
|
||||||
|
onSaved: (val) => _token = val,
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.only(top: 40.0, bottom: 20.0),
|
||||||
|
child: Text(
|
||||||
|
'Note: this will be changed to email/password in the future'),
|
||||||
|
),
|
||||||
|
/*
|
||||||
TextFormField(
|
TextFormField(
|
||||||
decoration: InputDecoration(labelText: 'Email'),
|
decoration: InputDecoration(labelText: 'Email'),
|
||||||
keyboardType: TextInputType.emailAddress,
|
keyboardType: TextInputType.emailAddress,
|
||||||
//contentPadding: EdgeInsets.fromLTRB(20.0, 10.0, 20.0, 10.0),
|
//contentPadding: EdgeInsets.fromLTRB(20.0, 10.0, 20.0, 10.0),
|
||||||
//autofocus: false,
|
//autofocus: false,
|
||||||
/*
|
|
||||||
validator: (val) =>
|
validator: (val) =>
|
||||||
val.isEmpty ? 'Please enter your email.' : null,
|
val.isEmpty ? 'Please enter your email.' : null,
|
||||||
*/
|
|
||||||
onSaved: (val) => _username = val,
|
onSaved: (val) => _username = val,
|
||||||
),
|
),
|
||||||
TextFormField(
|
TextFormField(
|
||||||
decoration: InputDecoration(labelText: 'Password'),
|
decoration: InputDecoration(labelText: 'Password'),
|
||||||
/*
|
|
||||||
validator: (val) =>
|
validator: (val) =>
|
||||||
val.isEmpty ? 'Please enter your password.' : null,
|
val.isEmpty ? 'Please enter your password.' : null,
|
||||||
*/
|
|
||||||
onSaved: (val) => _password = val,
|
onSaved: (val) => _password = val,
|
||||||
obscureText: true,
|
obscureText: true,
|
||||||
),
|
),
|
||||||
|
*/
|
||||||
Padding(
|
Padding(
|
||||||
padding: EdgeInsets.only(top: 20.0),
|
padding: EdgeInsets.only(top: 20.0),
|
||||||
child: Material(
|
child: Material(
|
||||||
|
|
@ -68,7 +90,7 @@ class _LoginScreenState extends State<LoginScreen> {
|
||||||
height: 42.0,
|
height: 42.0,
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
_submit();
|
_submit();
|
||||||
loginAction(context, _username, _password);
|
loginAction(context, _url, _token);
|
||||||
//Navigator.of(context).pushNamed(HomeScreen.tag);
|
//Navigator.of(context).pushNamed(HomeScreen.tag);
|
||||||
},
|
},
|
||||||
color: Colors.lightBlueAccent,
|
color: Colors.lightBlueAccent,
|
||||||
|
|
|
||||||
|
|
@ -27,8 +27,6 @@ class ProductList extends StatelessWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
ListView _buildListView() {
|
ListView _buildListView() {
|
||||||
print('_buildListView');
|
|
||||||
print(products);
|
|
||||||
return ListView.builder(
|
return ListView.builder(
|
||||||
key: NinjaKeys.productList,
|
key: NinjaKeys.productList,
|
||||||
itemCount: products.length,
|
itemCount: products.length,
|
||||||
|
|
|
||||||
|
|
@ -347,6 +347,13 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.0"
|
version: "3.0.0"
|
||||||
|
redux_logging:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: redux_logging
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.3.0"
|
||||||
shelf:
|
shelf:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ dependencies:
|
||||||
cupertino_icons: ^0.1.0
|
cupertino_icons: ^0.1.0
|
||||||
json_annotation: ^0.2.3
|
json_annotation: ^0.2.3
|
||||||
path_provider: "^0.4.0"
|
path_provider: "^0.4.0"
|
||||||
|
redux_logging: "^0.3.0"
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue