Refactored multiselect to use Actions

This commit is contained in:
Gianfranco Gasbarri 2019-09-25 13:13:04 +01:00
parent 9b4a2bf6e3
commit 848a73c935
9 changed files with 75 additions and 68 deletions

View File

@ -2,31 +2,31 @@ 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';
export 'package:invoiceninja_flutter/data/models/entities.dart';
export 'package:invoiceninja_flutter/data/models/product_model.dart';
export 'package:invoiceninja_flutter/data/models/client_model.dart'; export 'package:invoiceninja_flutter/data/models/client_model.dart';
export 'package:invoiceninja_flutter/data/models/company_model.dart'; export 'package:invoiceninja_flutter/data/models/company_model.dart';
export 'package:invoiceninja_flutter/data/models/credit_model.dart'; export 'package:invoiceninja_flutter/data/models/credit_model.dart';
export 'package:invoiceninja_flutter/data/models/project_model.dart';
export 'package:invoiceninja_flutter/data/models/payment_model.dart';
export 'package:invoiceninja_flutter/data/models/invoice_model.dart';
export 'package:invoiceninja_flutter/data/models/task_model.dart';
export 'package:invoiceninja_flutter/data/models/expense_model.dart';
export 'package:invoiceninja_flutter/data/models/vendor_model.dart';
export 'package:invoiceninja_flutter/data/models/document_model.dart'; export 'package:invoiceninja_flutter/data/models/document_model.dart';
export 'package:invoiceninja_flutter/data/models/static/static_data_model.dart'; export 'package:invoiceninja_flutter/data/models/entities.dart';
export 'package:invoiceninja_flutter/data/models/expense_model.dart';
export 'package:invoiceninja_flutter/data/models/invoice_model.dart';
export 'package:invoiceninja_flutter/data/models/payment_model.dart';
export 'package:invoiceninja_flutter/data/models/product_model.dart';
export 'package:invoiceninja_flutter/data/models/project_model.dart';
export 'package:invoiceninja_flutter/data/models/static/country_model.dart';
export 'package:invoiceninja_flutter/data/models/static/currency_model.dart'; export 'package:invoiceninja_flutter/data/models/static/currency_model.dart';
export 'package:invoiceninja_flutter/data/models/static/size_model.dart';
export 'package:invoiceninja_flutter/data/models/static/industry_model.dart';
export 'package:invoiceninja_flutter/data/models/static/timezone_model.dart';
export 'package:invoiceninja_flutter/data/models/static/date_format_model.dart'; export 'package:invoiceninja_flutter/data/models/static/date_format_model.dart';
export 'package:invoiceninja_flutter/data/models/static/datetime_format_model.dart'; export 'package:invoiceninja_flutter/data/models/static/datetime_format_model.dart';
export 'package:invoiceninja_flutter/data/models/static/language_model.dart'; export 'package:invoiceninja_flutter/data/models/static/frequency_model.dart';
export 'package:invoiceninja_flutter/data/models/static/payment_type_model.dart'; export 'package:invoiceninja_flutter/data/models/static/industry_model.dart';
export 'package:invoiceninja_flutter/data/models/static/country_model.dart';
export 'package:invoiceninja_flutter/data/models/static/invoice_design_model.dart'; export 'package:invoiceninja_flutter/data/models/static/invoice_design_model.dart';
export 'package:invoiceninja_flutter/data/models/static/invoice_status_model.dart'; export 'package:invoiceninja_flutter/data/models/static/invoice_status_model.dart';
export 'package:invoiceninja_flutter/data/models/static/frequency_model.dart'; export 'package:invoiceninja_flutter/data/models/static/language_model.dart';
export 'package:invoiceninja_flutter/data/models/static/payment_type_model.dart';
export 'package:invoiceninja_flutter/data/models/static/size_model.dart';
export 'package:invoiceninja_flutter/data/models/static/static_data_model.dart';
export 'package:invoiceninja_flutter/data/models/static/timezone_model.dart';
export 'package:invoiceninja_flutter/data/models/task_model.dart';
export 'package:invoiceninja_flutter/data/models/vendor_model.dart';
part 'models.g.dart'; part 'models.g.dart';
@ -59,6 +59,7 @@ class EntityAction extends EnumClass {
static const EntityAction start = _$start; static const EntityAction start = _$start;
static const EntityAction resume = _$resume; static const EntityAction resume = _$resume;
static const EntityAction stop = _$stop; static const EntityAction stop = _$stop;
static const EntityAction toggleMultiselect = _$toggleMultiselect;
static BuiltSet<EntityAction> get values => _$values; static BuiltSet<EntityAction> get values => _$values;
static EntityAction valueOf(String name) => _$valueOf(name); static EntityAction valueOf(String name) => _$valueOf(name);

View File

@ -30,6 +30,8 @@ const EntityAction _$more = const EntityAction._('more');
const EntityAction _$start = const EntityAction._('start'); const EntityAction _$start = const EntityAction._('start');
const EntityAction _$resume = const EntityAction._('resume'); const EntityAction _$resume = const EntityAction._('resume');
const EntityAction _$stop = const EntityAction._('stop'); const EntityAction _$stop = const EntityAction._('stop');
const EntityAction _$toggleMultiselect =
const EntityAction._('toggleMultiselect');
EntityAction _$valueOf(String name) { EntityAction _$valueOf(String name) {
switch (name) { switch (name) {
@ -81,6 +83,8 @@ EntityAction _$valueOf(String name) {
return _$resume; return _$resume;
case 'stop': case 'stop':
return _$stop; return _$stop;
case 'toggleMultiselect':
return _$toggleMultiselect;
default: default:
throw new ArgumentError(name); throw new ArgumentError(name);
} }
@ -112,6 +116,7 @@ final BuiltSet<EntityAction> _$values =
_$start, _$start,
_$resume, _$resume,
_$stop, _$stop,
_$toggleMultiselect,
]); ]);
Serializer<EntityAction> _$entityActionSerializer = Serializer<EntityAction> _$entityActionSerializer =

View File

@ -14,13 +14,12 @@ class S implements WidgetsLocalizations {
static S current; static S current;
static const GeneratedLocalizationsDelegate delegate = static const GeneratedLocalizationsDelegate delegate =
GeneratedLocalizationsDelegate(); GeneratedLocalizationsDelegate();
static S of(BuildContext context) => Localizations.of<S>(context, S); static S of(BuildContext context) => Localizations.of<S>(context, S);
@override @override
TextDirection get textDirection => TextDirection.ltr; TextDirection get textDirection => TextDirection.ltr;
} }
class $en extends S { class $en extends S {
@ -36,7 +35,8 @@ class GeneratedLocalizationsDelegate extends LocalizationsDelegate<S> {
]; ];
} }
LocaleListResolutionCallback listResolution({Locale fallback, bool withCountry = true}) { LocaleListResolutionCallback listResolution(
{Locale fallback, bool withCountry = true}) {
return (List<Locale> locales, Iterable<Locale> supported) { return (List<Locale> locales, Iterable<Locale> supported) {
if (locales == null || locales.isEmpty) { if (locales == null || locales.isEmpty) {
return fallback ?? supported.first; return fallback ?? supported.first;
@ -46,7 +46,8 @@ class GeneratedLocalizationsDelegate extends LocalizationsDelegate<S> {
}; };
} }
LocaleResolutionCallback resolution({Locale fallback, bool withCountry = true}) { LocaleResolutionCallback resolution(
{Locale fallback, bool withCountry = true}) {
return (Locale locale, Iterable<Locale> supported) { return (Locale locale, Iterable<Locale> supported) {
return _resolve(locale, fallback, supported, withCountry); return _resolve(locale, fallback, supported, withCountry);
}; };
@ -61,7 +62,7 @@ class GeneratedLocalizationsDelegate extends LocalizationsDelegate<S> {
S.current = const $en(); S.current = const $en();
return SynchronousFuture<S>(S.current); return SynchronousFuture<S>(S.current);
default: default:
// NO-OP. // NO-OP.
} }
} }
S.current = const S(); S.current = const S();
@ -77,7 +78,8 @@ class GeneratedLocalizationsDelegate extends LocalizationsDelegate<S> {
/// ///
/// Internal method to resolve a locale from a list of locales. /// Internal method to resolve a locale from a list of locales.
/// ///
Locale _resolve(Locale locale, Locale fallback, Iterable<Locale> supported, bool withCountry) { Locale _resolve(Locale locale, Locale fallback, Iterable<Locale> supported,
bool withCountry) {
if (locale == null || !_isSupported(locale, withCountry)) { if (locale == null || !_isSupported(locale, withCountry)) {
return fallback ?? supported.first; return fallback ?? supported.first;
} }
@ -110,7 +112,9 @@ class GeneratedLocalizationsDelegate extends LocalizationsDelegate<S> {
} }
// If no country requirement is requested, check if this locale has no country. // If no country requirement is requested, check if this locale has no country.
if (true != withCountry && (supportedLocale.countryCode == null || supportedLocale.countryCode.isEmpty)) { if (true != withCountry &&
(supportedLocale.countryCode == null ||
supportedLocale.countryCode.isEmpty)) {
return true; return true;
} }
} }
@ -120,7 +124,7 @@ class GeneratedLocalizationsDelegate extends LocalizationsDelegate<S> {
} }
String getLang(Locale l) => l == null String getLang(Locale l) => l == null
? null ? null
: l.countryCode != null && l.countryCode.isEmpty : l.countryCode != null && l.countryCode.isEmpty
? l.languageCode ? l.languageCode
: l.toString(); : l.toString();

View File

@ -324,6 +324,16 @@ void handleClientAction(
snackBarCompleter(context, localization.deletedClient), client.id)); snackBarCompleter(context, localization.deletedClient), client.id));
} }
break; break;
case EntityAction.toggleMultiselect:
if (!store.state.clientListState.isInMultiselect()) {
store.dispatch(StartMultiselect(context: context));
}
if (!store.state.clientListState.isSelected(client)) {
store.dispatch(AddToMultiselect(context: context, entity: client));
} else {
store.dispatch(RemoveFromMultiselect(context: context, entity: client));
}
break;
} }
} }

View File

@ -1,7 +1,7 @@
import 'dart:math'; import 'dart:math';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:invoiceninja_flutter/data/models/models.dart';
import 'package:flutter_slidable/flutter_slidable.dart'; import 'package:flutter_slidable/flutter_slidable.dart';
import 'package:invoiceninja_flutter/data/models/models.dart';
import 'package:invoiceninja_flutter/ui/app/lists/selected_indicator.dart'; import 'package:invoiceninja_flutter/ui/app/lists/selected_indicator.dart';
import 'package:invoiceninja_flutter/utils/localization.dart'; import 'package:invoiceninja_flutter/utils/localization.dart';
@ -36,6 +36,22 @@ class DismissibleEntity extends StatelessWidget {
actionPane: SlidableDrawerActionPane(), actionPane: SlidableDrawerActionPane(),
key: Key(entity.entityKey + Random().nextInt(100000).toString()), key: Key(entity.entityKey + Random().nextInt(100000).toString()),
actions: <Widget>[ actions: <Widget>[
IconSlideAction(
caption: localization.select,
color: Colors.teal,
foregroundColor: Colors.white,
icon: Icons.check_box,
onTap: () => onEntityAction(EntityAction.toggleMultiselect),
),
IconSlideAction(
caption: localization.more,
color: Colors.black45,
foregroundColor: Colors.white,
icon: Icons.more_vert,
onTap: () => onEntityAction(EntityAction.more),
),
],
secondaryActions: <Widget>[
entity.isActive entity.isActive
? IconSlideAction( ? IconSlideAction(
caption: localization.archive, caption: localization.archive,
@ -51,15 +67,6 @@ class DismissibleEntity extends StatelessWidget {
icon: Icons.restore, icon: Icons.restore,
onTap: () => onEntityAction(EntityAction.restore), onTap: () => onEntityAction(EntityAction.restore),
), ),
IconSlideAction(
caption: localization.more,
color: Colors.black45,
foregroundColor: Colors.white,
icon: Icons.more_vert,
onTap: () => onEntityAction(EntityAction.more),
),
],
secondaryActions: <Widget>[
entity.isDeleted ?? false entity.isDeleted ?? false
? IconSlideAction( ? IconSlideAction(
caption: localization.restore, caption: localization.restore,

View File

@ -3,7 +3,6 @@ import 'package:flutter/material.dart';
import 'package:flutter_redux/flutter_redux.dart'; import 'package:flutter_redux/flutter_redux.dart';
import 'package:invoiceninja_flutter/data/models/models.dart'; import 'package:invoiceninja_flutter/data/models/models.dart';
import 'package:invoiceninja_flutter/redux/app/app_state.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/entities/entity_actions_dialog.dart'; import 'package:invoiceninja_flutter/ui/app/entities/entity_actions_dialog.dart';
import 'package:invoiceninja_flutter/ui/app/help_text.dart'; import 'package:invoiceninja_flutter/ui/app/help_text.dart';
import 'package:invoiceninja_flutter/ui/app/lists/list_divider.dart'; import 'package:invoiceninja_flutter/ui/app/lists/list_divider.dart';
@ -12,7 +11,6 @@ import 'package:invoiceninja_flutter/ui/app/loading_indicator.dart';
import 'package:invoiceninja_flutter/ui/client/client_list_vm.dart'; import 'package:invoiceninja_flutter/ui/client/client_list_vm.dart';
import 'package:invoiceninja_flutter/ui/client/client_list_item.dart'; import 'package:invoiceninja_flutter/ui/client/client_list_item.dart';
import 'package:invoiceninja_flutter/utils/localization.dart'; import 'package:invoiceninja_flutter/utils/localization.dart';
import 'package:redux/src/store.dart';
import 'client_list_vm.dart'; import 'client_list_vm.dart';
@ -79,7 +77,8 @@ class ClientList extends StatelessWidget {
context, client, action); context, client, action);
} }
}, },
onLongPress: () => _onLongPress(context, store), onLongPress: () => viewModel.onEntityAction(
context, client, EntityAction.toggleMultiselect),
); );
}, },
), ),
@ -88,12 +87,4 @@ class ClientList extends StatelessWidget {
], ],
); );
} }
void _onLongPress(
BuildContext context, Store<AppState> store, BaseEntity entity) {
if (!store.state.clientListState.isInMultiselect()) {
store.dispatch(StartMultiselect(context: context));
store.dispatch(AddToMultiselect(context: context, entity: entity));
}
}
} }

View File

@ -54,15 +54,20 @@ class ClientListItem extends StatelessWidget {
//entityKey: clientItemKey, //entityKey: clientItemKey,
child: ListTile( child: ListTile(
onTap: isInMultiselect onTap: isInMultiselect
? () => _toggleSelection(context, store, client) ? () => onEntityAction(EntityAction.toggleMultiselect)
: onTap, : onTap,
onLongPress: onLongPress, onLongPress: onLongPress,
title: Row( title: Row(
children: <Widget>[ children: <Widget>[
if (isInMultiselect) if (isInMultiselect)
IgnorePointer( IgnorePointer(
child: Checkbox( child: Padding(
value: listUIState.isSelected(client), onChanged: null)), padding: const EdgeInsets.only(right: 12.0),
child: Checkbox(
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
value: listUIState.isSelected(client),
onChanged: null),
)),
Expanded( Expanded(
child: Text( child: Text(
client.displayName, client.displayName,
@ -91,13 +96,4 @@ class ClientListItem extends StatelessWidget {
), ),
); );
} }
void _toggleSelection(
BuildContext context, Store<AppState> store, BaseEntity entity) {
if (store.state.clientListState.isSelected(entity)) {
store.dispatch(RemoveFromMultiselect(context: context, entity: entity));
} else {
store.dispatch(AddToMultiselect(context: context, entity: entity));
}
}
} }

View File

@ -103,13 +103,6 @@ class ClientScreen extends StatelessWidget {
); );
} }
void _onSelectPressed(
String value, Store<AppState> store, BuildContext context) {
if (value == 'select' && !store.state.clientListState.isInMultiselect()) {
store.dispatch(StartMultiselect(context: context));
}
}
void _finishMultiselect(BuildContext context, ListMultiselectButtonMode mode, void _finishMultiselect(BuildContext context, ListMultiselectButtonMode mode,
Store<AppState> store) async { Store<AppState> store) async {
if (mode == ListMultiselectButtonMode.DONE) { if (mode == ListMultiselectButtonMode.DONE) {

View File

@ -497,7 +497,7 @@ mixin LocalizationsProvider on LocaleCodeAware {
'marked_quote_as_sent': 'Successfully marked quote as sent', 'marked_quote_as_sent': 'Successfully marked quote as sent',
'expired': 'Expired', 'expired': 'Expired',
'all': 'All', 'all': 'All',
'select': 'Select...' 'select': 'Select'
}, },
'sq': { 'sq': {
'thank_you_for_your_purchase': 'Thank you for your purchase!', 'thank_you_for_your_purchase': 'Thank you for your purchase!',