Working on dashboard
This commit is contained in:
parent
15775f1421
commit
314171504e
|
|
@ -3,18 +3,31 @@ import 'package:json_annotation/json_annotation.dart';
|
|||
part 'entities.g.dart';
|
||||
|
||||
@JsonSerializable()
|
||||
class BaseResponse extends Object with _$BaseResponseSerializerMixin {
|
||||
class BaseListResponse extends Object with _$BaseListResponseSerializerMixin {
|
||||
//final String message;
|
||||
|
||||
@JsonKey(name: "data")
|
||||
final List<dynamic> data;
|
||||
|
||||
BaseResponse(
|
||||
//this.message,
|
||||
BaseListResponse(
|
||||
this.data,
|
||||
);
|
||||
|
||||
factory BaseResponse.fromJson(Map<String, dynamic> json) => _$BaseResponseFromJson(json);
|
||||
factory BaseListResponse.fromJson(Map<String, dynamic> json) => _$BaseListResponseFromJson(json);
|
||||
}
|
||||
|
||||
@JsonSerializable()
|
||||
class BaseItemResponse extends Object with _$BaseItemResponseSerializerMixin {
|
||||
//final String message;
|
||||
|
||||
@JsonKey(name: "data")
|
||||
final dynamic data;
|
||||
|
||||
BaseItemResponse(
|
||||
this.data,
|
||||
);
|
||||
|
||||
factory BaseItemResponse.fromJson(Map<String, dynamic> json) => _$BaseItemResponseFromJson(json);
|
||||
}
|
||||
|
||||
@JsonSerializable()
|
||||
|
|
|
|||
|
|
@ -6,14 +6,22 @@ part of 'entities.dart';
|
|||
// Generator: JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
BaseResponse _$BaseResponseFromJson(Map<String, dynamic> json) =>
|
||||
new BaseResponse(json['data'] as List);
|
||||
BaseListResponse _$BaseListResponseFromJson(Map<String, dynamic> json) =>
|
||||
new BaseListResponse(json['data'] as List);
|
||||
|
||||
abstract class _$BaseResponseSerializerMixin {
|
||||
abstract class _$BaseListResponseSerializerMixin {
|
||||
List<dynamic> get data;
|
||||
Map<String, dynamic> toJson() => <String, dynamic>{'data': data};
|
||||
}
|
||||
|
||||
BaseItemResponse _$BaseItemResponseFromJson(Map<String, dynamic> json) =>
|
||||
new BaseItemResponse(json['data']);
|
||||
|
||||
abstract class _$BaseItemResponseSerializerMixin {
|
||||
dynamic get data;
|
||||
Map<String, dynamic> toJson() => <String, dynamic>{'data': data};
|
||||
}
|
||||
|
||||
ProductEntity _$ProductEntityFromJson(Map<String, dynamic> json) =>
|
||||
new ProductEntity(json['id'] as int)
|
||||
..productKey = json['product_key'] as String
|
||||
|
|
|
|||
|
|
@ -0,0 +1,60 @@
|
|||
import 'dart:async';
|
||||
import 'dart:core';
|
||||
import 'package:meta/meta.dart';
|
||||
|
||||
import 'package:invoiceninja/redux/auth/auth_state.dart';
|
||||
import 'package:invoiceninja/data/models/entities.dart';
|
||||
import 'package:invoiceninja/data/repositories/repositories.dart';
|
||||
import 'package:invoiceninja/data/file_storage.dart';
|
||||
import 'package:invoiceninja/data/web_client.dart';
|
||||
|
||||
/// A class that glues together our local file storage and web client. It has a
|
||||
/// clear responsibility: Load Dashboard and Persist products.
|
||||
class DashboardRepositoryFlutter implements BaseRepository {
|
||||
final FileStorage fileStorage;
|
||||
final WebClient webClient;
|
||||
|
||||
const DashboardRepositoryFlutter({
|
||||
@required this.fileStorage,
|
||||
this.webClient = const WebClient(),
|
||||
});
|
||||
|
||||
/// Loads products first from File storage. If they don't exist or encounter an
|
||||
/// error, it attempts to load the Dashboard from a Web Client.
|
||||
@override
|
||||
Future<dynamic> loadItem(AuthState auth) async {
|
||||
print('ProductRepo: loadDashboard...');
|
||||
|
||||
final data = await webClient.fetchItem(
|
||||
auth.url + '/dashboard', auth.token);
|
||||
|
||||
//fileStorage.saveDashboard(products);
|
||||
print('== LOAD DASHBOARD ==');
|
||||
print(data);
|
||||
return data.map((data) => ProductEntity.fromJson(data)).toList();
|
||||
|
||||
/*
|
||||
try {
|
||||
return await fileStorage.loadData();
|
||||
} catch (exception) {
|
||||
final products = await webClient.fetchData(
|
||||
auth.url + '/products', auth.token);
|
||||
|
||||
//fileStorage.saveDashboard(products);
|
||||
|
||||
return products;
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<dynamic>> loadItems(dynamic product) {
|
||||
return Future.wait<dynamic>([]);
|
||||
}
|
||||
|
||||
// Persists products to local disk and the web
|
||||
@override
|
||||
Future saveData(List<dynamic> products) {
|
||||
return Future.wait<dynamic>([]);
|
||||
}
|
||||
}
|
||||
|
|
@ -22,10 +22,10 @@ class ProductsRepositoryFlutter implements BaseRepository {
|
|||
/// 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.
|
||||
@override
|
||||
Future<List<dynamic>> loadData(AuthState auth) async {
|
||||
Future<List<dynamic>> loadItems(AuthState auth) async {
|
||||
print('ProductRepo: loadProducts...');
|
||||
|
||||
final products = await webClient.fetchData(
|
||||
final products = await webClient.fetchList(
|
||||
auth.url + '/products', auth.token);
|
||||
|
||||
//fileStorage.saveProducts(products);
|
||||
|
|
@ -46,7 +46,11 @@ class ProductsRepositoryFlutter implements BaseRepository {
|
|||
*/
|
||||
}
|
||||
|
||||
// Persists products to local disk and the web
|
||||
@override
|
||||
Future loadItem(dynamic product) {
|
||||
return Future.wait<dynamic>([]);
|
||||
}
|
||||
|
||||
@override
|
||||
Future saveData(List<dynamic> products) {
|
||||
return Future.wait<dynamic>([
|
||||
|
|
|
|||
|
|
@ -13,10 +13,10 @@ import 'package:invoiceninja/data/models/entities.dart';
|
|||
/// inject the correct implementation depending on the environment, such as
|
||||
/// web or Flutter.
|
||||
abstract class BaseRepository {
|
||||
/// 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.
|
||||
Future<List<dynamic>> loadData(AuthState auth);
|
||||
|
||||
// Persists products to local disk and the web
|
||||
Future<List<dynamic>> loadItems(AuthState auth);
|
||||
|
||||
Future<dynamic> loadItem(AuthState auth);
|
||||
|
||||
Future saveData(List<dynamic> data);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,15 +12,23 @@ class WebClient {
|
|||
|
||||
const WebClient();
|
||||
|
||||
/// Mock that "fetches" some Products from a "web service" after a short delay
|
||||
Future<List<dynamic>> fetchData(String url, String token) async {
|
||||
|
||||
final response = await http.Client().get(
|
||||
Future<http.Response> sendRequest(String url, String token) {
|
||||
return http.Client().get(
|
||||
url,
|
||||
headers: {'X-Ninja-Token': token},
|
||||
);
|
||||
}
|
||||
|
||||
return BaseResponse
|
||||
Future<dynamic> fetchItem(String url, String token) async {
|
||||
final http.Response response = await sendRequest(url, token);
|
||||
return BaseItemResponse
|
||||
.fromJson(json.decode(response.body))
|
||||
.data;
|
||||
}
|
||||
|
||||
Future<List<dynamic>> fetchList(String url, String token) async {
|
||||
final http.Response response = await sendRequest(url, token);
|
||||
return BaseListResponse
|
||||
.fromJson(json.decode(response.body))
|
||||
.data
|
||||
.toList();
|
||||
|
|
|
|||
|
|
@ -1,17 +1,20 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_redux/flutter_redux.dart';
|
||||
import 'package:redux/redux.dart';
|
||||
import 'package:invoiceninja/redux/app/app_state.dart';
|
||||
import 'package:redux_logging/redux_logging.dart';
|
||||
import 'package:invoiceninja/routes.dart';
|
||||
import 'package:invoiceninja/ui/auth/login.dart';
|
||||
import 'package:invoiceninja/ui/dashboard/dashboard.dart';
|
||||
import 'package:invoiceninja/ui/client/clients.dart';
|
||||
import 'package:invoiceninja/ui/product/products.dart';
|
||||
import 'package:invoiceninja/ui/auth/login.dart';
|
||||
import 'package:invoiceninja/routes.dart';
|
||||
import 'package:invoiceninja/redux/app/app_reducer.dart';
|
||||
import 'package:invoiceninja/redux/app/app_state.dart';
|
||||
import 'package:invoiceninja/redux/auth/auth_middleware.dart';
|
||||
import 'package:invoiceninja/redux/dashboard/dashboard_actions.dart';
|
||||
import 'package:invoiceninja/redux/dashboard/dashboard_middleware.dart';
|
||||
import 'package:invoiceninja/redux/product/product_actions.dart';
|
||||
import 'package:invoiceninja/redux/product/product_middleware.dart';
|
||||
import 'package:invoiceninja/redux/auth/auth_middleware.dart';
|
||||
import 'package:invoiceninja/redux/app/app_reducer.dart';
|
||||
import 'package:redux_logging/redux_logging.dart';
|
||||
|
||||
|
||||
void main() {
|
||||
|
||||
|
|
@ -29,6 +32,7 @@ class InvoiceNinjaApp extends StatelessWidget {
|
|||
initialState: AppState.loading(),
|
||||
middleware: []
|
||||
..addAll(createStoreAuthMiddleware())
|
||||
..addAll(createStoreDashboardMiddleware())
|
||||
..addAll(createStoreProductsMiddleware())
|
||||
..addAll([
|
||||
LoggingMiddleware.printer(),
|
||||
|
|
@ -59,6 +63,7 @@ class InvoiceNinjaApp extends StatelessWidget {
|
|||
},
|
||||
AppRoutes.dashboard: (context) {
|
||||
return StoreBuilder<AppState>(
|
||||
onInit: (store) => store.dispatch(LoadDashboardAction()),
|
||||
builder: (context, store) {
|
||||
return Dashboard();
|
||||
},
|
||||
|
|
|
|||
|
|
@ -0,0 +1,25 @@
|
|||
import 'package:invoiceninja/data/models/models.dart';
|
||||
|
||||
class LoadDashboardAction {}
|
||||
|
||||
class DashboardNotLoadedAction {
|
||||
final dynamic error;
|
||||
|
||||
DashboardNotLoadedAction(this.error);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'DashboardNotLoadedAction{products: $error}';
|
||||
}
|
||||
}
|
||||
|
||||
class DashboardLoadedAction {
|
||||
final List<ProductEntity> products;
|
||||
|
||||
DashboardLoadedAction(this.products);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'DashboardLoadedAction{products: $products}';
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:redux/redux.dart';
|
||||
import 'package:invoiceninja/redux/dashboard/dashboard_actions.dart';
|
||||
import 'package:invoiceninja/redux/app/app_state.dart';
|
||||
import 'package:invoiceninja/data/repositories/repositories.dart';
|
||||
import 'package:invoiceninja/data/repositories/dashboard_repository.dart';
|
||||
import 'package:invoiceninja/data/file_storage.dart';
|
||||
import 'package:invoiceninja/redux/product/product_selectors.dart';
|
||||
import 'package:invoiceninja/data/models/entities.dart';
|
||||
|
||||
List<Middleware<AppState>> createStoreDashboardMiddleware([
|
||||
BaseRepository repository = const DashboardRepositoryFlutter(
|
||||
fileStorage: const FileStorage(
|
||||
'__invoiceninja__',
|
||||
getApplicationDocumentsDirectory,
|
||||
),
|
||||
),
|
||||
]) {
|
||||
final loadDashboard = _createLoadDashboard(repository);
|
||||
|
||||
return [
|
||||
TypedMiddleware<AppState, LoadDashboardAction>(loadDashboard),
|
||||
];
|
||||
}
|
||||
|
||||
Middleware<AppState> _createSaveDashboard(BaseRepository repository) {
|
||||
return (Store<AppState> store, action, NextDispatcher next) {
|
||||
next(action);
|
||||
|
||||
/*
|
||||
repository.saveDashboard(
|
||||
productsSelector(store.state).map((product) => product.toEntity()).toList(),
|
||||
);
|
||||
*/
|
||||
};
|
||||
}
|
||||
|
||||
Middleware<AppState> _createLoadDashboard(BaseRepository repository) {
|
||||
return (Store<AppState> store, action, NextDispatcher next) {
|
||||
|
||||
repository.loadItems(store.state.auth).then(
|
||||
(data) {
|
||||
store.dispatch(
|
||||
DashboardLoadedAction(
|
||||
//products.map(ProductEntity.fromEntity).toList(),
|
||||
data,
|
||||
),
|
||||
);
|
||||
},
|
||||
).catchError((error) => store.dispatch(DashboardNotLoadedAction(error)));
|
||||
|
||||
next(action);
|
||||
};
|
||||
}
|
||||
|
|
@ -40,9 +40,7 @@ Middleware<AppState> _createSaveProducts(BaseRepository repository) {
|
|||
Middleware<AppState> _createLoadProducts(BaseRepository repository) {
|
||||
return (Store<AppState> store, action, NextDispatcher next) {
|
||||
|
||||
print('Product Middleware: createLoadProducts...');
|
||||
|
||||
repository.loadData(store.state.auth).then(
|
||||
repository.loadItems(store.state.auth).then(
|
||||
(products) {
|
||||
store.dispatch(
|
||||
ProductsLoadedAction(
|
||||
|
|
|
|||
|
|
@ -7,21 +7,28 @@ class Dashboard extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return new Scaffold(
|
||||
appBar: new AppBar(
|
||||
title: new Text('Dashboard'),
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text('Dashboard'),
|
||||
),
|
||||
drawer: new Sidebar(),
|
||||
body: new Center(
|
||||
child: new Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
new Text(
|
||||
'DASHBOARD',
|
||||
drawer: Sidebar(),
|
||||
body: Column(
|
||||
children: <Widget>[
|
||||
Card(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
const ListTile(
|
||||
leading: const Icon(Icons.album),
|
||||
title: const Text('The Enchanted Nightingale'),
|
||||
subtitle: const Text(
|
||||
'Music by Julie Gable. Lyrics by Sidney Stein.'),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue