diff --git a/lib/ui/app/entity_top_filter.dart b/lib/ui/app/entity_top_filter.dart index 8dbf4769d..eee8994f8 100644 --- a/lib/ui/app/entity_top_filter.dart +++ b/lib/ui/app/entity_top_filter.dart @@ -15,7 +15,9 @@ import 'package:invoiceninja_flutter/ui/app/presenters/entity_presenter.dart'; import 'package:invoiceninja_flutter/utils/localization.dart'; class EntityTopFilter extends StatelessWidget { - const EntityTopFilter({@required this.show}); + const EntityTopFilter({ + @required this.show, + }); final bool show; @@ -229,3 +231,181 @@ class EntityTopFilter extends StatelessWidget { ); } } + +class EntityBottomFilter extends StatelessWidget { + const EntityBottomFilter(); + + @override + Widget build(BuildContext context) { + final localization = AppLocalization.of(context); + final store = StoreProvider.of(context); + final state = store.state; + final uiState = state.uiState; + final prefState = state.prefState; + + final filterEntityType = uiState.filterEntityType; + final routeEntityType = uiState.entityTypeRoute; + + final entityMap = + filterEntityType != null ? state.getEntityMap(filterEntityType) : null; + final filterEntity = + entityMap != null ? entityMap[uiState.filterEntityId] : null; + final relatedTypes = filterEntityType?.relatedTypes + ?.where((element) => state.company.isModuleEnabled(element)) + ?.toList() ?? + []; + + final backgroundColor = !prefState.enableDarkMode && state.hasAccentColor + ? state.accentColor + : Theme.of(context).cardColor; + + return Material( + color: backgroundColor, + child: Row( + children: [ + if (!prefState.isFilterVisible) + InkWell( + onTap: () { + store.dispatch(UpdateUserPreferences( + isFilterVisible: !prefState.isFilterVisible)); + }, + onLongPress: () { + editEntity(context: context, entity: filterEntity); + }, + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SizedBox(width: 12), + Icon( + Icons.chrome_reader_mode, + color: state.headerTextColor, + ), + SizedBox(width: 12), + ConstrainedBox( + constraints: BoxConstraints(maxWidth: 220), + child: Text( + EntityPresenter() + .initialize(filterEntity, context) + .title(), + style: + TextStyle(fontSize: 17, color: state.headerTextColor), + overflow: TextOverflow.ellipsis, + maxLines: 1, + ), + ), + SizedBox(width: 12), + ], + ), + ), + SizedBox(width: 12), + Expanded( + child: Align( + alignment: Alignment.centerLeft, + child: OverflowView.flexible( + spacing: 4, + children: [ + for (int i = 0; i < relatedTypes.length; i++) + DecoratedBox( + child: TextButton( + child: Text( + localization.lookup('${relatedTypes[i].plural}'), + style: TextStyle( + color: state.headerTextColor, + ), + ), + style: TextButton.styleFrom( + padding: EdgeInsets.symmetric(horizontal: 12), + minimumSize: Size(0, 36), + ), + onPressed: () { + viewEntitiesByType( + entityType: relatedTypes[i], + filterEntity: filterEntity, + ); + }, + onLongPress: () { + handleEntityAction(filterEntity, + EntityAction.newEntityType(relatedTypes[i])); + }, + ), + decoration: BoxDecoration( + border: relatedTypes[i] == routeEntityType + ? Border( + bottom: BorderSide( + color: prefState.enableDarkMode || + !state.hasAccentColor + ? state.accentColor + : Colors.white, + width: 2, + ), + ) + : null, + ), + ) + ], + builder: (context, remaining) { + return PopupMenuButton( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 4), + child: Row( + children: [ + Text( + localization.more, + style: TextStyle(color: state.headerTextColor), + ), + SizedBox(width: 4), + Icon(Icons.arrow_drop_down, + color: state.headerTextColor), + ], + ), + ), + initialValue: routeEntityType, + onSelected: (EntityType value) { + if (value == filterEntityType) { + viewEntity( + entity: filterEntity, + ); + } else { + viewEntitiesByType( + entityType: value, + filterEntity: filterEntity, + ); + } + }, + itemBuilder: (BuildContext context) => filterEntityType + .relatedTypes + .sublist(relatedTypes.length - remaining) + .where( + (element) => state.company.isModuleEnabled(element)) + .map((type) => PopupMenuItem( + value: type, + child: ConstrainedBox( + constraints: BoxConstraints( + minWidth: 75, + ), + child: Text(type == filterEntityType + ? localization.overview + : '${localization.lookup(type.plural)}'), + ), + )) + .toList(), + ); + }, + ), + ), + ), + SizedBox(width: 4), + IconButton( + icon: Icon( + Icons.clear, + color: state.headerTextColor, + ), + onPressed: () => store.dispatch( + FilterByEntity(entity: uiState.filterEntity), + ), + ), + ], + ), + ); + } +} diff --git a/lib/ui/client/view/client_view.dart b/lib/ui/client/view/client_view.dart index 7e185f7e2..75e3b03f3 100644 --- a/lib/ui/client/view/client_view.dart +++ b/lib/ui/client/view/client_view.dart @@ -10,6 +10,8 @@ import 'package:invoiceninja_flutter/redux/app/app_actions.dart'; import 'package:invoiceninja_flutter/redux/app/app_state.dart'; import 'package:invoiceninja_flutter/redux/client/client_actions.dart'; import 'package:invoiceninja_flutter/ui/app/buttons/bottom_buttons.dart'; +import 'package:invoiceninja_flutter/ui/app/entity_top_filter.dart'; +import 'package:invoiceninja_flutter/ui/app/presenters/entity_presenter.dart'; import 'package:invoiceninja_flutter/ui/app/view_scaffold.dart'; import 'package:invoiceninja_flutter/ui/client/view/client_view_activity.dart'; import 'package:invoiceninja_flutter/ui/client/view/client_view_details.dart'; @@ -83,15 +85,22 @@ class _ClientViewState extends State Widget build(BuildContext context) { final localization = AppLocalization.of(context); final store = StoreProvider.of(context); + final state = store.state; final viewModel = widget.viewModel; final client = viewModel.client; final documents = client.documents; final userCompany = viewModel.state.userCompany; if (widget.isTopFilter) { - return ViewScaffold( - body: Placeholder(), - entity: client, + return Material( + child: Column( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + EntityBottomFilter(), + Expanded(child: Placeholder()), + ], + ), ); }