Sorting..
This commit is contained in:
parent
ef98ff9a4a
commit
ecc68040f1
|
|
@ -23,12 +23,12 @@ abstract class ProductItemResponse implements Built<ProductItemResponse, Product
|
||||||
}
|
}
|
||||||
|
|
||||||
class ProductFields {
|
class ProductFields {
|
||||||
static String productKey = 'productKey';
|
static const String productKey = 'productKey';
|
||||||
static String notes = 'notes';
|
static const String notes = 'notes';
|
||||||
static String cost = 'cost';
|
static const String cost = 'cost';
|
||||||
static String updatedAt = 'updatedAt';
|
static const String updatedAt = 'updatedAt';
|
||||||
static String archivedAt = 'archivedAt';
|
static const String archivedAt = 'archivedAt';
|
||||||
static String isDeleted = 'isDeleted';
|
static const String isDeleted = 'isDeleted';
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class ProductEntity implements Built<ProductEntity, ProductEntityBuilder> {
|
abstract class ProductEntity implements Built<ProductEntity, ProductEntityBuilder> {
|
||||||
|
|
|
||||||
|
|
@ -31,8 +31,6 @@ class WebClient {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
print('== pOST ==');
|
|
||||||
print(data);
|
|
||||||
try {
|
try {
|
||||||
final jsonResponse = json.decode(response.body);
|
final jsonResponse = json.decode(response.body);
|
||||||
return jsonResponse;
|
return jsonResponse;
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ class InvoiceNinjaApp extends StatelessWidget {
|
||||||
..addAll(createStoreDashboardMiddleware())
|
..addAll(createStoreDashboardMiddleware())
|
||||||
..addAll(createStoreProductsMiddleware())
|
..addAll(createStoreProductsMiddleware())
|
||||||
..addAll([
|
..addAll([
|
||||||
LoggingMiddleware.printer(),
|
//LoggingMiddleware.printer(),
|
||||||
])
|
])
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,23 @@
|
||||||
|
import 'package:built_collection/built_collection.dart';
|
||||||
import 'package:built_value/built_value.dart';
|
import 'package:built_value/built_value.dart';
|
||||||
import 'package:built_value/serializer.dart';
|
import 'package:built_value/serializer.dart';
|
||||||
|
import 'package:invoiceninja/ui/app/action_popup_menu.dart';
|
||||||
|
|
||||||
part 'entity_ui_state.g.dart';
|
part 'entity_ui_state.g.dart';
|
||||||
|
|
||||||
abstract class EntityUIState implements Built<EntityUIState, EntityUIStateBuilder> {
|
abstract class EntityUIState implements Built<EntityUIState, EntityUIStateBuilder> {
|
||||||
|
|
||||||
String get sortField;
|
String get sortField;
|
||||||
|
bool get sortAscending;
|
||||||
|
BuiltList<int> get stateFilterIds;
|
||||||
|
BuiltList<int> get statusFilterIds;
|
||||||
|
|
||||||
factory EntityUIState(sortField) {
|
factory EntityUIState(sortField) {
|
||||||
return _$EntityUIState._(
|
return _$EntityUIState._(
|
||||||
sortField: sortField,
|
sortField: sortField,
|
||||||
|
sortAscending: true,
|
||||||
|
stateFilterIds: BuiltList<int>(),
|
||||||
|
statusFilterIds: BuiltList<int>(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,17 @@ class _$EntityUIStateSerializer implements StructuredSerializer<EntityUIState> {
|
||||||
'sortField',
|
'sortField',
|
||||||
serializers.serialize(object.sortField,
|
serializers.serialize(object.sortField,
|
||||||
specifiedType: const FullType(String)),
|
specifiedType: const FullType(String)),
|
||||||
|
'sortAscending',
|
||||||
|
serializers.serialize(object.sortAscending,
|
||||||
|
specifiedType: const FullType(bool)),
|
||||||
|
'stateFilterIds',
|
||||||
|
serializers.serialize(object.stateFilterIds,
|
||||||
|
specifiedType:
|
||||||
|
const FullType(BuiltList, const [const FullType(int)])),
|
||||||
|
'statusFilterIds',
|
||||||
|
serializers.serialize(object.statusFilterIds,
|
||||||
|
specifiedType:
|
||||||
|
const FullType(BuiltList, const [const FullType(int)])),
|
||||||
];
|
];
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|
@ -50,6 +61,22 @@ class _$EntityUIStateSerializer implements StructuredSerializer<EntityUIState> {
|
||||||
result.sortField = serializers.deserialize(value,
|
result.sortField = serializers.deserialize(value,
|
||||||
specifiedType: const FullType(String)) as String;
|
specifiedType: const FullType(String)) as String;
|
||||||
break;
|
break;
|
||||||
|
case 'sortAscending':
|
||||||
|
result.sortAscending = serializers.deserialize(value,
|
||||||
|
specifiedType: const FullType(bool)) as bool;
|
||||||
|
break;
|
||||||
|
case 'stateFilterIds':
|
||||||
|
result.stateFilterIds.replace(serializers.deserialize(value,
|
||||||
|
specifiedType:
|
||||||
|
const FullType(BuiltList, const [const FullType(int)]))
|
||||||
|
as BuiltList);
|
||||||
|
break;
|
||||||
|
case 'statusFilterIds':
|
||||||
|
result.statusFilterIds.replace(serializers.deserialize(value,
|
||||||
|
specifiedType:
|
||||||
|
const FullType(BuiltList, const [const FullType(int)]))
|
||||||
|
as BuiltList);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -60,13 +87,30 @@ class _$EntityUIStateSerializer implements StructuredSerializer<EntityUIState> {
|
||||||
class _$EntityUIState extends EntityUIState {
|
class _$EntityUIState extends EntityUIState {
|
||||||
@override
|
@override
|
||||||
final String sortField;
|
final String sortField;
|
||||||
|
@override
|
||||||
|
final bool sortAscending;
|
||||||
|
@override
|
||||||
|
final BuiltList<int> stateFilterIds;
|
||||||
|
@override
|
||||||
|
final BuiltList<int> statusFilterIds;
|
||||||
|
|
||||||
factory _$EntityUIState([void updates(EntityUIStateBuilder b)]) =>
|
factory _$EntityUIState([void updates(EntityUIStateBuilder b)]) =>
|
||||||
(new EntityUIStateBuilder()..update(updates)).build();
|
(new EntityUIStateBuilder()..update(updates)).build();
|
||||||
|
|
||||||
_$EntityUIState._({this.sortField}) : super._() {
|
_$EntityUIState._(
|
||||||
|
{this.sortField,
|
||||||
|
this.sortAscending,
|
||||||
|
this.stateFilterIds,
|
||||||
|
this.statusFilterIds})
|
||||||
|
: super._() {
|
||||||
if (sortField == null)
|
if (sortField == null)
|
||||||
throw new BuiltValueNullFieldError('EntityUIState', 'sortField');
|
throw new BuiltValueNullFieldError('EntityUIState', 'sortField');
|
||||||
|
if (sortAscending == null)
|
||||||
|
throw new BuiltValueNullFieldError('EntityUIState', 'sortAscending');
|
||||||
|
if (stateFilterIds == null)
|
||||||
|
throw new BuiltValueNullFieldError('EntityUIState', 'stateFilterIds');
|
||||||
|
if (statusFilterIds == null)
|
||||||
|
throw new BuiltValueNullFieldError('EntityUIState', 'statusFilterIds');
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
@ -80,18 +124,27 @@ class _$EntityUIState extends EntityUIState {
|
||||||
bool operator ==(dynamic other) {
|
bool operator ==(dynamic other) {
|
||||||
if (identical(other, this)) return true;
|
if (identical(other, this)) return true;
|
||||||
if (other is! EntityUIState) return false;
|
if (other is! EntityUIState) return false;
|
||||||
return sortField == other.sortField;
|
return sortField == other.sortField &&
|
||||||
|
sortAscending == other.sortAscending &&
|
||||||
|
stateFilterIds == other.stateFilterIds &&
|
||||||
|
statusFilterIds == other.statusFilterIds;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode {
|
int get hashCode {
|
||||||
return $jf($jc(0, sortField.hashCode));
|
return $jf($jc(
|
||||||
|
$jc($jc($jc(0, sortField.hashCode), sortAscending.hashCode),
|
||||||
|
stateFilterIds.hashCode),
|
||||||
|
statusFilterIds.hashCode));
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return (newBuiltValueToStringHelper('EntityUIState')
|
return (newBuiltValueToStringHelper('EntityUIState')
|
||||||
..add('sortField', sortField))
|
..add('sortField', sortField)
|
||||||
|
..add('sortAscending', sortAscending)
|
||||||
|
..add('stateFilterIds', stateFilterIds)
|
||||||
|
..add('statusFilterIds', statusFilterIds))
|
||||||
.toString();
|
.toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -104,11 +157,31 @@ class EntityUIStateBuilder
|
||||||
String get sortField => _$this._sortField;
|
String get sortField => _$this._sortField;
|
||||||
set sortField(String sortField) => _$this._sortField = sortField;
|
set sortField(String sortField) => _$this._sortField = sortField;
|
||||||
|
|
||||||
|
bool _sortAscending;
|
||||||
|
bool get sortAscending => _$this._sortAscending;
|
||||||
|
set sortAscending(bool sortAscending) =>
|
||||||
|
_$this._sortAscending = sortAscending;
|
||||||
|
|
||||||
|
ListBuilder<int> _stateFilterIds;
|
||||||
|
ListBuilder<int> get stateFilterIds =>
|
||||||
|
_$this._stateFilterIds ??= new ListBuilder<int>();
|
||||||
|
set stateFilterIds(ListBuilder<int> stateFilterIds) =>
|
||||||
|
_$this._stateFilterIds = stateFilterIds;
|
||||||
|
|
||||||
|
ListBuilder<int> _statusFilterIds;
|
||||||
|
ListBuilder<int> get statusFilterIds =>
|
||||||
|
_$this._statusFilterIds ??= new ListBuilder<int>();
|
||||||
|
set statusFilterIds(ListBuilder<int> statusFilterIds) =>
|
||||||
|
_$this._statusFilterIds = statusFilterIds;
|
||||||
|
|
||||||
EntityUIStateBuilder();
|
EntityUIStateBuilder();
|
||||||
|
|
||||||
EntityUIStateBuilder get _$this {
|
EntityUIStateBuilder get _$this {
|
||||||
if (_$v != null) {
|
if (_$v != null) {
|
||||||
_sortField = _$v.sortField;
|
_sortField = _$v.sortField;
|
||||||
|
_sortAscending = _$v.sortAscending;
|
||||||
|
_stateFilterIds = _$v.stateFilterIds?.toBuilder();
|
||||||
|
_statusFilterIds = _$v.statusFilterIds?.toBuilder();
|
||||||
_$v = null;
|
_$v = null;
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
|
|
@ -127,7 +200,27 @@ class EntityUIStateBuilder
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_$EntityUIState build() {
|
_$EntityUIState build() {
|
||||||
final _$result = _$v ?? new _$EntityUIState._(sortField: sortField);
|
_$EntityUIState _$result;
|
||||||
|
try {
|
||||||
|
_$result = _$v ??
|
||||||
|
new _$EntityUIState._(
|
||||||
|
sortField: sortField,
|
||||||
|
sortAscending: sortAscending,
|
||||||
|
stateFilterIds: stateFilterIds.build(),
|
||||||
|
statusFilterIds: statusFilterIds.build());
|
||||||
|
} catch (_) {
|
||||||
|
String _$failedField;
|
||||||
|
try {
|
||||||
|
_$failedField = 'stateFilterIds';
|
||||||
|
stateFilterIds.build();
|
||||||
|
_$failedField = 'statusFilterIds';
|
||||||
|
statusFilterIds.build();
|
||||||
|
} catch (e) {
|
||||||
|
throw new BuiltValueNestedFieldError(
|
||||||
|
'EntityUIState', _$failedField, e.toString());
|
||||||
|
}
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
replace(_$result);
|
replace(_$result);
|
||||||
return _$result;
|
return _$result;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import 'package:invoiceninja/redux/app/entity_ui_state.dart';
|
import 'package:invoiceninja/redux/app/entity_ui_state.dart';
|
||||||
|
import 'package:invoiceninja/ui/app/action_popup_menu.dart';
|
||||||
import 'package:redux/redux.dart';
|
import 'package:redux/redux.dart';
|
||||||
import 'package:invoiceninja/redux/product/product_actions.dart';
|
import 'package:invoiceninja/redux/product/product_actions.dart';
|
||||||
import 'package:invoiceninja/redux/product/product_state.dart';
|
import 'package:invoiceninja/redux/product/product_state.dart';
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,8 @@
|
||||||
|
import 'package:built_collection/built_collection.dart';
|
||||||
import 'package:invoiceninja/data/models/models.dart';
|
import 'package:invoiceninja/data/models/models.dart';
|
||||||
import 'package:invoiceninja/redux/app/app_state.dart';
|
import 'package:invoiceninja/redux/app/app_state.dart';
|
||||||
|
import 'package:invoiceninja/redux/app/entity_ui_state.dart';
|
||||||
|
import 'package:invoiceninja/redux/product/product_state.dart';
|
||||||
|
|
||||||
bool isLoadingSelector(AppState state) => state.isLoading;
|
bool isLoadingSelector(AppState state) => state.isLoading;
|
||||||
|
|
||||||
|
|
@ -10,21 +13,26 @@ List<ProductEntity> productsSelector(AppState state) =>
|
||||||
state.productState().list.map((id) => state.productState().map[id]);
|
state.productState().list.map((id) => state.productState().map[id]);
|
||||||
|
|
||||||
List<ProductEntity> filteredProductsSelector(
|
List<ProductEntity> filteredProductsSelector(
|
||||||
List<ProductEntity> products,
|
ProductState productState,
|
||||||
//VisibilityFilter activeFilter,
|
EntityUIState productUIState) {
|
||||||
) {
|
|
||||||
return products.where((product) {
|
var list = productState.list.toList();
|
||||||
return true;
|
|
||||||
/*
|
list.sort((productAId, productBId) {
|
||||||
if (activeFilter == VisibilityFilter.all) {
|
var productA = productState.map[productAId];
|
||||||
return true;
|
var productB = productState.map[productBId];
|
||||||
} else if (activeFilter == VisibilityFilter.active) {
|
var sortField = productUIState.sortField;
|
||||||
return !product.complete;
|
|
||||||
} else if (activeFilter == VisibilityFilter.completed) {
|
switch (sortField) {
|
||||||
return product.complete;
|
case ProductFields.productKey:
|
||||||
|
return productA.productKey.compareTo(productB.productKey);
|
||||||
|
case ProductFields.cost:
|
||||||
|
return productA.cost.compareTo(productB.cost);
|
||||||
}
|
}
|
||||||
*/
|
});
|
||||||
}).toList();
|
|
||||||
|
print('== SORTING LIST');
|
||||||
|
return list.map((id) => productState.map[id]).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
||||||
|
|
@ -6,12 +6,6 @@ enum ActionMenuButtonType {
|
||||||
sort,
|
sort,
|
||||||
}
|
}
|
||||||
|
|
||||||
class SortField {
|
|
||||||
final String field;
|
|
||||||
final String label;
|
|
||||||
SortField(this.field, this.label);
|
|
||||||
}
|
|
||||||
|
|
||||||
class ActionMenuChoice {
|
class ActionMenuChoice {
|
||||||
const ActionMenuChoice(this.action, {this.label, this.icon});
|
const ActionMenuChoice(this.action, {this.label, this.icon});
|
||||||
final String label;
|
final String label;
|
||||||
|
|
@ -24,8 +18,8 @@ class ActionMenuButton extends StatelessWidget {
|
||||||
final List<ActionMenuChoice> actions;
|
final List<ActionMenuChoice> actions;
|
||||||
final Function onSelected;
|
final Function onSelected;
|
||||||
|
|
||||||
final List<SortField> sortFields;
|
final List<String> sortFields;
|
||||||
final Function(String) onSelectedSort;
|
final Function(String, Function) onSelectedSort;
|
||||||
final String selectedSort;
|
final String selectedSort;
|
||||||
|
|
||||||
ActionMenuButton({
|
ActionMenuButton({
|
||||||
|
|
@ -38,30 +32,52 @@ class ActionMenuButton extends StatelessWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
_showSortScreen() {
|
print('=== BUILD ===');
|
||||||
|
|
||||||
|
showSortScreen() {
|
||||||
scaffoldKey.currentState.showBottomSheet((context) {
|
scaffoldKey.currentState.showBottomSheet((context) {
|
||||||
return Container(
|
return Container(
|
||||||
color: Colors.grey[200],
|
color: Colors.grey[200],
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: sortFields.map((sortField) {
|
children: sortFields.map((sortField) {
|
||||||
print('field: ' + sortField.field );
|
|
||||||
print('match: ' + (sortField.field == selectedSort ? 'yes' : 'no'));
|
|
||||||
return RadioListTile(
|
return RadioListTile(
|
||||||
dense: true,
|
dense: true,
|
||||||
title: Text(sortField.label),
|
title: Text(AppLocalization.of((context)).lookup(sortField)),
|
||||||
groupValue: selectedSort,
|
groupValue: selectedSort,
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
print('value changed: ' + value);
|
this.onSelectedSort(value, showSortScreen);
|
||||||
this.onSelectedSort(value);
|
|
||||||
},
|
},
|
||||||
value: sortField.field,
|
value: sortField,
|
||||||
);
|
);
|
||||||
}).toList()),
|
}).toList()),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
showFIlterScree() {
|
||||||
|
scaffoldKey.currentState.showBottomSheet((context) {
|
||||||
|
bool _active = false;
|
||||||
|
|
||||||
|
return Container(
|
||||||
|
color: Colors.grey[200],
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
CheckboxListTile(
|
||||||
|
value: true,
|
||||||
|
title: Text('Active'),
|
||||||
|
selected: _active,
|
||||||
|
onChanged: (checked) {
|
||||||
|
_active = ! checked;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return PopupMenuButton<ActionMenuChoice>(
|
return PopupMenuButton<ActionMenuChoice>(
|
||||||
itemBuilder: (BuildContext context) {
|
itemBuilder: (BuildContext context) {
|
||||||
return actions.map((ActionMenuChoice choice) {
|
return actions.map((ActionMenuChoice choice) {
|
||||||
|
|
@ -91,9 +107,10 @@ class ActionMenuButton extends StatelessWidget {
|
||||||
onSelected: (ActionMenuChoice choice) {
|
onSelected: (ActionMenuChoice choice) {
|
||||||
switch (choice.action) {
|
switch (choice.action) {
|
||||||
case ActionMenuButtonType.sort:
|
case ActionMenuButtonType.sort:
|
||||||
_showSortScreen();
|
showSortScreen();
|
||||||
break;
|
break;
|
||||||
case ActionMenuButtonType.filter:
|
case ActionMenuButtonType.filter:
|
||||||
|
showFIlterScree();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,77 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:invoiceninja/utils/localization.dart';
|
||||||
|
|
||||||
|
/*
|
||||||
|
enum BottomBarButtonType {
|
||||||
|
filter,
|
||||||
|
sort,
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
class _AppBottomBarChoice {
|
||||||
|
const _AppBottomBarChoice (this.type, {this.label, this.icon});
|
||||||
|
final String label;
|
||||||
|
final IconData icon;
|
||||||
|
final BottomBarButtonType type;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
class AppBottomBar extends StatelessWidget {
|
||||||
|
final GlobalKey<ScaffoldState> scaffoldKey;
|
||||||
|
//final List<ActionMenuChoice> actions;
|
||||||
|
|
||||||
|
final List<String> sortFields;
|
||||||
|
final Function(String) onSelectedSort;
|
||||||
|
final String selectedSort;
|
||||||
|
|
||||||
|
AppBottomBar(
|
||||||
|
{
|
||||||
|
//this.actions,
|
||||||
|
//this.onSelected,
|
||||||
|
this.scaffoldKey,
|
||||||
|
this.sortFields,
|
||||||
|
this.onSelectedSort,
|
||||||
|
this.selectedSort});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return new BottomAppBar(
|
||||||
|
hasNotch: true,
|
||||||
|
child: Row(
|
||||||
|
children: <Widget>[
|
||||||
|
IconButton(
|
||||||
|
tooltip: AppLocalization.of((context)).sort,
|
||||||
|
icon: Icon(Icons.sort_by_alpha),
|
||||||
|
onPressed: () {
|
||||||
|
scaffoldKey.currentState.showBottomSheet((context) {
|
||||||
|
return Container(
|
||||||
|
color: Colors.grey[200],
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: sortFields.map((sortField) {
|
||||||
|
return RadioListTile(
|
||||||
|
dense: true,
|
||||||
|
title: Text(
|
||||||
|
AppLocalization.of((context)).lookup(sortField)),
|
||||||
|
groupValue: selectedSort,
|
||||||
|
onChanged: (value) {
|
||||||
|
this.onSelectedSort(value);
|
||||||
|
},
|
||||||
|
value: sortField,
|
||||||
|
);
|
||||||
|
}).toList()),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
tooltip: AppLocalization.of((context)).filter,
|
||||||
|
icon: Icon(Icons.filter_list),
|
||||||
|
onPressed: () {},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -24,9 +24,9 @@ class ProductList extends StatelessWidget {
|
||||||
onRefresh: () => viewModel.onRefreshed(context),
|
onRefresh: () => viewModel.onRefreshed(context),
|
||||||
child: ListView.builder(
|
child: ListView.builder(
|
||||||
key: NinjaKeys.productList,
|
key: NinjaKeys.productList,
|
||||||
itemCount: viewModel.productState.list.length,
|
itemCount: viewModel.products.length,
|
||||||
itemBuilder: (BuildContext context, int index) {
|
itemBuilder: (BuildContext context, index) {
|
||||||
final product = viewModel.productState.map[viewModel.productState.list[index]];
|
var product = viewModel.products[index];
|
||||||
return Column(children: <Widget>[
|
return Column(children: <Widget>[
|
||||||
ProductItem(
|
ProductItem(
|
||||||
product: product,
|
product: product,
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
import 'package:invoiceninja/redux/product/product_selectors.dart';
|
||||||
import 'package:invoiceninja/ui/app/snackbar_row.dart';
|
import 'package:invoiceninja/ui/app/snackbar_row.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
|
|
@ -30,14 +31,14 @@ class ProductListBuilder extends StatelessWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class ProductListVM {
|
class ProductListVM {
|
||||||
final ProductState productState;
|
final List<ProductEntity> products;
|
||||||
final bool isLoading;
|
final bool isLoading;
|
||||||
final Function(BuildContext, ProductEntity) onProductTap;
|
final Function(BuildContext, ProductEntity) onProductTap;
|
||||||
final Function(BuildContext, ProductEntity, DismissDirection) onDismissed;
|
final Function(BuildContext, ProductEntity, DismissDirection) onDismissed;
|
||||||
final Function(BuildContext) onRefreshed;
|
final Function(BuildContext) onRefreshed;
|
||||||
|
|
||||||
ProductListVM({
|
ProductListVM({
|
||||||
@required this.productState,
|
@required this.products,
|
||||||
@required this.isLoading,
|
@required this.isLoading,
|
||||||
@required this.onProductTap,
|
@required this.onProductTap,
|
||||||
@required this.onDismissed,
|
@required this.onDismissed,
|
||||||
|
|
@ -58,13 +59,7 @@ class ProductListVM {
|
||||||
}
|
}
|
||||||
|
|
||||||
return ProductListVM(
|
return ProductListVM(
|
||||||
productState: store.state.productState(),
|
products: filteredProductsSelector(store.state.productState(), store.state.productUIState()),
|
||||||
/*
|
|
||||||
products: filteredProductsSelector(
|
|
||||||
productsSelector(store.state),
|
|
||||||
//activeFilterSelector(store.state),
|
|
||||||
),
|
|
||||||
*/
|
|
||||||
isLoading: store.state.productState().lastUpdated == 0,
|
isLoading: store.state.productState().lastUpdated == 0,
|
||||||
onProductTap: (context, product) {
|
onProductTap: (context, product) {
|
||||||
store.dispatch(SelectProductAction(product));
|
store.dispatch(SelectProductAction(product));
|
||||||
|
|
|
||||||
|
|
@ -10,11 +10,13 @@ import 'package:flutter_redux/flutter_redux.dart';
|
||||||
import 'package:invoiceninja/redux/product/product_actions.dart';
|
import 'package:invoiceninja/redux/product/product_actions.dart';
|
||||||
import 'package:invoiceninja/ui/app/app_drawer_vm.dart';
|
import 'package:invoiceninja/ui/app/app_drawer_vm.dart';
|
||||||
import 'package:invoiceninja/ui/app/action_popup_menu.dart';
|
import 'package:invoiceninja/ui/app/action_popup_menu.dart';
|
||||||
|
import 'package:invoiceninja/ui/app/app_bottom_bar.dart';
|
||||||
|
|
||||||
class ProductScreen extends StatelessWidget {
|
class ProductScreen extends StatelessWidget {
|
||||||
ProductScreen() : super(key: NinjaKeys.productHome);
|
ProductScreen() : super(key: NinjaKeys.productHome);
|
||||||
|
|
||||||
final _scaffoldKey = GlobalKey<ScaffoldState>();
|
final _scaffoldKey = GlobalKey<ScaffoldState>();
|
||||||
|
//static ActionMenuButtonType _activeSheet;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
|
@ -27,34 +29,35 @@ class ProductScreen extends StatelessWidget {
|
||||||
icon: Icon(Icons.search),
|
icon: Icon(Icons.search),
|
||||||
onPressed: () {},
|
onPressed: () {},
|
||||||
),
|
),
|
||||||
StoreConnector(
|
/*
|
||||||
converter: (Store<AppState> store) => store,
|
ActionMenuButton(
|
||||||
builder: (context, store) {
|
onSelectedSort: (value, callback) {
|
||||||
return ActionMenuButton(
|
StoreProvider.of<AppState>(context).dispatch(SortProducts(value));
|
||||||
scaffoldKey: _scaffoldKey,
|
//callback();
|
||||||
onSelectedSort: (value) {
|
|
||||||
store.dispatch(SortProducts(value));
|
|
||||||
},
|
},
|
||||||
selectedSort: store.state.productUIState().sortField,
|
|
||||||
sortFields: [
|
|
||||||
SortField(ProductFields.productKey,
|
|
||||||
AppLocalization.of((context)).product),
|
|
||||||
SortField(
|
|
||||||
ProductFields.cost, AppLocalization.of((context)).cost),
|
|
||||||
],
|
|
||||||
actions: [
|
actions: [
|
||||||
ActionMenuChoice(ActionMenuButtonType.sort),
|
ActionMenuChoice(ActionMenuButtonType.sort),
|
||||||
ActionMenuChoice(ActionMenuButtonType.filter),
|
ActionMenuChoice(ActionMenuButtonType.filter),
|
||||||
],
|
],
|
||||||
onSelected: (ActionMenuChoice choice) {},
|
onSelected: (ActionMenuChoice choice) {},
|
||||||
);
|
)
|
||||||
},
|
*/
|
||||||
), //FilterSelector(visible: activeTab == AppTab.products),
|
|
||||||
//ExtraActionsContainer(),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
drawer: AppDrawerBuilder(),
|
drawer: AppDrawerBuilder(),
|
||||||
body: ProductListBuilder(),
|
body: ProductListBuilder(),
|
||||||
|
bottomNavigationBar: AppBottomBar(
|
||||||
|
scaffoldKey: _scaffoldKey,
|
||||||
|
selectedSort: StoreProvider.of<AppState>(context).state.productUIState().sortField,
|
||||||
|
onSelectedSort: (value) {
|
||||||
|
StoreProvider.of<AppState>(context).dispatch(SortProducts(value));
|
||||||
|
},
|
||||||
|
sortFields: [
|
||||||
|
ProductFields.productKey,
|
||||||
|
ProductFields.cost,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
floatingActionButtonLocation: FloatingActionButtonLocation.endDocked,
|
||||||
floatingActionButton: StoreConnector(
|
floatingActionButton: StoreConnector(
|
||||||
converter: (Store<AppState> store) => store,
|
converter: (Store<AppState> store) => store,
|
||||||
builder: (context, store) {
|
builder: (context, store) {
|
||||||
|
|
@ -67,7 +70,7 @@ class ProductScreen extends StatelessWidget {
|
||||||
MaterialPageRoute(builder: (_) => ProductDetailsBuilder()));
|
MaterialPageRoute(builder: (_) => ProductDetailsBuilder()));
|
||||||
},
|
},
|
||||||
child: Icon(Icons.add),
|
child: Icon(Icons.add),
|
||||||
//tooltip: ArchSampleLocalizations.of(context).addProduct,
|
tooltip: AppLocalization.of(context).newProduct,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ class AppLocalization {
|
||||||
'new_product': 'New Product',
|
'new_product': 'New Product',
|
||||||
'product': 'Product',
|
'product': 'Product',
|
||||||
'products': 'Products',
|
'products': 'Products',
|
||||||
|
'productKey': 'Product Key',
|
||||||
'notes': 'Notes',
|
'notes': 'Notes',
|
||||||
'cost': 'Cost',
|
'cost': 'Cost',
|
||||||
'clients': 'Clients',
|
'clients': 'Clients',
|
||||||
|
|
@ -52,6 +53,7 @@ class AppLocalization {
|
||||||
String get newProduct => _localizedValues[locale.languageCode]['new_product'];
|
String get newProduct => _localizedValues[locale.languageCode]['new_product'];
|
||||||
String get product => _localizedValues[locale.languageCode]['product'];
|
String get product => _localizedValues[locale.languageCode]['product'];
|
||||||
String get products => _localizedValues[locale.languageCode]['products'];
|
String get products => _localizedValues[locale.languageCode]['products'];
|
||||||
|
String get productKey => _localizedValues[locale.languageCode]['product_key'];
|
||||||
String get notes => _localizedValues[locale.languageCode]['notes'];
|
String get notes => _localizedValues[locale.languageCode]['notes'];
|
||||||
String get cost => _localizedValues[locale.languageCode]['cost'];
|
String get cost => _localizedValues[locale.languageCode]['cost'];
|
||||||
String get clients => _localizedValues[locale.languageCode]['clients'];
|
String get clients => _localizedValues[locale.languageCode]['clients'];
|
||||||
|
|
@ -62,7 +64,9 @@ class AppLocalization {
|
||||||
String get successfullyDeletedProduct=> _localizedValues[locale.languageCode]['successfully_deleted_product'];
|
String get successfullyDeletedProduct=> _localizedValues[locale.languageCode]['successfully_deleted_product'];
|
||||||
String get successfullyRestoredProduct => _localizedValues[locale.languageCode]['successfully_restored_product'];
|
String get successfullyRestoredProduct => _localizedValues[locale.languageCode]['successfully_restored_product'];
|
||||||
|
|
||||||
|
String lookup(String key) {
|
||||||
|
return _localizedValues[locale.languageCode][key] ?? 'Missing: ' + key;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class AppLocalizationsDelegate extends LocalizationsDelegate<AppLocalization> {
|
class AppLocalizationsDelegate extends LocalizationsDelegate<AppLocalization> {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue