diff --git a/lib/data/models/serializers.dart b/lib/data/models/serializers.dart index 6a078f7c5..9cfdca4c9 100644 --- a/lib/data/models/serializers.dart +++ b/lib/data/models/serializers.dart @@ -31,6 +31,9 @@ import 'package:invoiceninja_flutter/redux/project/project_state.dart'; import 'package:invoiceninja_flutter/redux/payment/payment_state.dart'; import 'package:invoiceninja_flutter/redux/quote/quote_state.dart'; // STARTER: import - do not remove comment +import 'package:invoiceninja_flutter/data/models/company_gateway_model.dart'; +import 'package:invoiceninja_flutter/redux/company_gateway/company_gateway_state.dart'; + import 'package:invoiceninja_flutter/redux/group/group_state.dart'; import 'package:invoiceninja_flutter/redux/document/document_state.dart'; @@ -100,6 +103,8 @@ part 'serializers.g.dart'; DocumentListResponse, DocumentItemResponse, // STARTER: serializers - do not remove comment +CompanyGatewayEntity, + ]) final Serializers serializers = (_$serializers.toBuilder()..addPlugin(StandardJsonPlugin())).build(); diff --git a/lib/data/models/serializers.g.dart b/lib/data/models/serializers.g.dart index a802f73c8..be26be905 100644 --- a/lib/data/models/serializers.g.dart +++ b/lib/data/models/serializers.g.dart @@ -17,6 +17,9 @@ Serializers _$serializers = (new Serializers().toBuilder() ..add(ClientState.serializer) ..add(ClientUIState.serializer) ..add(CompanyEntity.serializer) + ..add(CompanyGatewayEntity.serializer) + ..add(CompanyGatewayState.serializer) + ..add(CompanyGatewayUIState.serializer) ..add(CompanyItemResponse.serializer) ..add(ContactEntity.serializer) ..add(CountryEntity.serializer) @@ -312,6 +315,8 @@ Serializers _$serializers = (new Serializers().toBuilder() ..addBuilderFactory( const FullType(BuiltMap, const [const FullType(String), const FullType(ClientEntity)]), () => new MapBuilder()) ..addBuilderFactory(const FullType(BuiltList, const [const FullType(String)]), () => new ListBuilder()) + ..addBuilderFactory(const FullType(BuiltMap, const [const FullType(String), const FullType(CompanyGatewayEntity)]), () => new MapBuilder()) + ..addBuilderFactory(const FullType(BuiltList, const [const FullType(String)]), () => new ListBuilder()) ..addBuilderFactory(const FullType(BuiltMap, const [const FullType(String), const FullType(CurrencyEntity)]), () => new MapBuilder()) ..addBuilderFactory(const FullType(BuiltMap, const [const FullType(String), const FullType(SizeEntity)]), () => new MapBuilder()) ..addBuilderFactory(const FullType(BuiltMap, const [const FullType(String), const FullType(IndustryEntity)]), () => new MapBuilder()) diff --git a/lib/data/repositories/company_gateway_repository.dart b/lib/data/repositories/company_gateway_repository.dart new file mode 100644 index 000000000..a58faea12 --- /dev/null +++ b/lib/data/repositories/company_gateway_repository.dart @@ -0,0 +1,70 @@ +import 'dart:async'; +import 'dart:convert'; +import 'dart:core'; +import 'package:built_collection/built_collection.dart'; +import 'package:invoiceninja_flutter/constants.dart'; +import 'package:invoiceninja_flutter/data/models/serializers.dart'; +import 'package:invoiceninja_flutter/redux/app/app_state.dart'; +import 'package:invoiceninja_flutter/data/models/models.dart'; +import 'package:invoiceninja_flutter/data/web_client.dart'; + +class CompanyGatewayRepository { + + const CompanyGatewayRepository({ + this.webClient = const WebClient(), + }); + + final WebClient webClient; + + Future loadItem( + Credentials credentials, String entityId) async { + final dynamic response = await webClient.get( + '${credentials.url}/company_gateways/$entityId', credentials.token); + + final CompanyGatewayItemResponse companyGatewayResponse = + serializers.deserializeWith(CompanyGatewayItemResponse.serializer, response); + + return companyGatewayResponse.data; + } + + Future> loadList( + Credentials credentials, int updatedAt) async { + String url = credentials.url + '/company_gateways?'; + + if (updatedAt > 0) { + url += '&updated_at=${updatedAt - kUpdatedAtBufferSeconds}'; + } + + final dynamic response = await webClient.get(url, credentials.token); + + final CompanyGatewayListResponse companyGatewayResponse = + serializers.deserializeWith(CompanyGatewayListResponse.serializer, response); + + return companyGatewayResponse.data; + } + + Future saveData( + Credentials credentials, CompanyGatewayEntity companyGateway, + [EntityAction action]) async { + final data = serializers.serializeWith(CompanyGatewayEntity.serializer, companyGateway); + dynamic response; + + if (companyGateway.isNew) { + response = await webClient.post( + credentials.url + '/company_gateways', + credentials.token, + data: json.encode(data)); + } else { + var url = credentials.url + '/company_gateways/' + companyGateway.id.toString(); + if (action != null) { + url += '?action=' + action.toString(); + } + response = await webClient.put(url, credentials.token, data: json.encode(data)); + } + + final CompanyGatewayItemResponse companyGatewayResponse = + serializers.deserializeWith(CompanyGatewayItemResponse.serializer, response); + + return companyGatewayResponse.data; + } +} diff --git a/lib/main.dart b/lib/main.dart index c22b5960b..8d6b13dcb 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -60,6 +60,12 @@ import 'package:redux_logging/redux_logging.dart'; import 'package:sentry/sentry.dart'; import 'package:shared_preferences/shared_preferences.dart'; // STARTER: import - do not remove comment +import 'package:invoiceninja_flutter/ui/company_gateway/company_gateway_screen.dart'; +import 'package:invoiceninja_flutter/ui/company_gateway/edit/company_gateway_edit_vm.dart'; +import 'package:invoiceninja_flutter/ui/company_gateway/view/company_gateway_view_vm.dart'; +import 'package:invoiceninja_flutter/redux/company_gateway/company_gateway_actions.dart'; +import 'package:invoiceninja_flutter/redux/company_gateway/company_gateway_middleware.dart'; + void main({bool isTesting = false}) async { final SentryClient _sentry = Config.SENTRY_DNS.isEmpty @@ -102,6 +108,8 @@ void main({bool isTesting = false}) async { ..addAll(createStoreQuotesMiddleware()) ..addAll(createStoreSettingsMiddleware()) // STARTER: middleware - do not remove comment +..addAll(createStoreCompanyGatewaysMiddleware()) + ..addAll(createStoreGroupsMiddleware()) ..addAll(isTesting ? [] @@ -317,6 +325,10 @@ class InvoiceNinjaAppState extends State { QuoteEditScreen.route: (context) => QuoteEditScreen(), QuoteEmailScreen.route: (context) => QuoteEmailScreen(), // STARTER: routes - do not remove comment +CompanyGatewayScreen.route: (context) => CompanyGatewayScreen(), +CompanyGatewayViewScreen.route: (context) => CompanyGatewayViewScreen(), +CompanyGatewayEditScreen.route: (context) => CompanyGatewayEditScreen(), + GroupSettingsScreen.route: (context) => GroupSettingsScreen(), GroupViewScreen.route: (context) => GroupViewScreen(), GroupEditScreen.route: (context) => GroupEditScreen(), diff --git a/lib/redux/app/app_state.dart b/lib/redux/app/app_state.dart index 3045f349e..1ceba9da1 100644 --- a/lib/redux/app/app_state.dart +++ b/lib/redux/app/app_state.dart @@ -33,6 +33,8 @@ import 'package:invoiceninja_flutter/ui/client/edit/client_edit_vm.dart'; import 'package:invoiceninja_flutter/ui/group/edit/group_edit_vm.dart'; import 'package:invoiceninja_flutter/ui/product/edit/product_edit_vm.dart'; // STARTER: import - do not remove comment +import 'package:invoiceninja_flutter/redux/company_gateway/company_gateway_state.dart'; + import 'package:invoiceninja_flutter/redux/group/group_state.dart'; part 'app_state.g.dart'; @@ -143,6 +145,9 @@ abstract class AppState implements Built { case EntityType.invoice: return invoiceUIState; // STARTER: states switch - do not remove comment +case EntityType.companyGateway: +return companyGatewayUIState; + case EntityType.group: return groupUIState; @@ -188,6 +193,11 @@ abstract class AppState implements Built { ListUIState get invoiceListState => uiState.invoiceUIState.listUIState; // STARTER: state getters - do not remove comment +CompanyGatewayState get companyGatewayState => selectedCompanyState.companyGatewayState; +ListUIState get companyGatewayListState => uiState.companyGatewayUIState.listUIState; +CompanyGatewayUIState get companyGatewayUIState => uiState.companyGatewayUIState; + + GroupState get groupState => selectedCompanyState.groupState; ListUIState get groupListState => uiState.groupUIState.listUIState; diff --git a/lib/redux/company/company_reducer.dart b/lib/redux/company/company_reducer.dart index df6dd6177..8beec7cd1 100644 --- a/lib/redux/company/company_reducer.dart +++ b/lib/redux/company/company_reducer.dart @@ -17,6 +17,8 @@ import 'package:invoiceninja_flutter/redux/project/project_reducer.dart'; import 'package:invoiceninja_flutter/redux/payment/payment_reducer.dart'; import 'package:invoiceninja_flutter/redux/quote/quote_reducer.dart'; // STARTER: import - do not remove comment +import 'package:invoiceninja_flutter/redux/company_gateway/company_gateway_reducer.dart'; + import 'package:invoiceninja_flutter/redux/group/group_reducer.dart'; UserCompanyState companyReducer(UserCompanyState state, dynamic action) { @@ -35,6 +37,8 @@ UserCompanyState companyReducer(UserCompanyState state, dynamic action) { ..vendorState.replace(vendorsReducer(state.vendorState, action)) ..taskState.replace(tasksReducer(state.taskState, action)) // STARTER: reducer - do not remove comment +..companyGatewayState.replace(companyGatewaysReducer(state.companyGatewayState, action)) + ..projectState.replace(projectsReducer(state.projectState, action)) ..paymentState.replace(paymentsReducer(state.paymentState, action)) ..quoteState.replace(quotesReducer(state.quoteState, action)) diff --git a/lib/redux/company/company_state.dart b/lib/redux/company/company_state.dart index 29de4f525..619777ed2 100644 --- a/lib/redux/company/company_state.dart +++ b/lib/redux/company/company_state.dart @@ -9,6 +9,8 @@ import 'package:built_value/built_value.dart'; import 'package:built_value/serializer.dart'; // STARTER: import - do not remove comment +import 'package:invoiceninja_flutter/redux/company_gateway/company_gateway_state.dart'; + import 'package:invoiceninja_flutter/redux/group/group_state.dart'; import 'package:invoiceninja_flutter/redux/document/document_state.dart'; @@ -44,6 +46,8 @@ abstract class UserCompanyState paymentState: PaymentState(), quoteState: QuoteState(), // STARTER: constructor - do not remove comment +companyGatewayState: CompanyGatewayState(), + groupState: GroupState(), ); } @@ -76,6 +80,8 @@ abstract class UserCompanyState QuoteState get quoteState; // STARTER: fields - do not remove comment +CompanyGatewayState get companyGatewayState; + GroupState get groupState; CompanyEntity get company => userCompany.company; diff --git a/lib/redux/company/company_state.g.dart b/lib/redux/company/company_state.g.dart index 10ba68796..1cfc75eef 100644 --- a/lib/redux/company/company_state.g.dart +++ b/lib/redux/company/company_state.g.dart @@ -55,6 +55,9 @@ class _$UserCompanyStateSerializer 'quoteState', serializers.serialize(object.quoteState, specifiedType: const FullType(QuoteState)), + 'companyGatewayState', + serializers.serialize(object.companyGatewayState, + specifiedType: const FullType(CompanyGatewayState)), 'groupState', serializers.serialize(object.groupState, specifiedType: const FullType(GroupState)), @@ -129,6 +132,11 @@ class _$UserCompanyStateSerializer result.quoteState.replace(serializers.deserialize(value, specifiedType: const FullType(QuoteState)) as QuoteState); break; + case 'companyGatewayState': + result.companyGatewayState.replace(serializers.deserialize(value, + specifiedType: const FullType(CompanyGatewayState)) + as CompanyGatewayState); + break; case 'groupState': result.groupState.replace(serializers.deserialize(value, specifiedType: const FullType(GroupState)) as GroupState); @@ -273,6 +281,8 @@ class _$UserCompanyState extends UserCompanyState { @override final QuoteState quoteState; @override + final CompanyGatewayState companyGatewayState; + @override final GroupState groupState; factory _$UserCompanyState( @@ -292,6 +302,7 @@ class _$UserCompanyState extends UserCompanyState { this.projectState, this.paymentState, this.quoteState, + this.companyGatewayState, this.groupState}) : super._() { if (documentState == null) { @@ -327,6 +338,10 @@ class _$UserCompanyState extends UserCompanyState { if (quoteState == null) { throw new BuiltValueNullFieldError('UserCompanyState', 'quoteState'); } + if (companyGatewayState == null) { + throw new BuiltValueNullFieldError( + 'UserCompanyState', 'companyGatewayState'); + } if (groupState == null) { throw new BuiltValueNullFieldError('UserCompanyState', 'groupState'); } @@ -356,6 +371,7 @@ class _$UserCompanyState extends UserCompanyState { projectState == other.projectState && paymentState == other.paymentState && quoteState == other.quoteState && + companyGatewayState == other.companyGatewayState && groupState == other.groupState; } @@ -373,19 +389,23 @@ class _$UserCompanyState extends UserCompanyState { $jc( $jc( $jc( - $jc(0, - userCompany.hashCode), - documentState.hashCode), - dashboardState.hashCode), - productState.hashCode), - clientState.hashCode), - invoiceState.hashCode), - expenseState.hashCode), - vendorState.hashCode), - taskState.hashCode), - projectState.hashCode), - paymentState.hashCode), - quoteState.hashCode), + $jc( + $jc( + 0, + userCompany + .hashCode), + documentState.hashCode), + dashboardState.hashCode), + productState.hashCode), + clientState.hashCode), + invoiceState.hashCode), + expenseState.hashCode), + vendorState.hashCode), + taskState.hashCode), + projectState.hashCode), + paymentState.hashCode), + quoteState.hashCode), + companyGatewayState.hashCode), groupState.hashCode)); } @@ -404,6 +424,7 @@ class _$UserCompanyState extends UserCompanyState { ..add('projectState', projectState) ..add('paymentState', paymentState) ..add('quoteState', quoteState) + ..add('companyGatewayState', companyGatewayState) ..add('groupState', groupState)) .toString(); } @@ -484,6 +505,12 @@ class UserCompanyStateBuilder set quoteState(QuoteStateBuilder quoteState) => _$this._quoteState = quoteState; + CompanyGatewayStateBuilder _companyGatewayState; + CompanyGatewayStateBuilder get companyGatewayState => + _$this._companyGatewayState ??= new CompanyGatewayStateBuilder(); + set companyGatewayState(CompanyGatewayStateBuilder companyGatewayState) => + _$this._companyGatewayState = companyGatewayState; + GroupStateBuilder _groupState; GroupStateBuilder get groupState => _$this._groupState ??= new GroupStateBuilder(); @@ -506,6 +533,7 @@ class UserCompanyStateBuilder _projectState = _$v.projectState?.toBuilder(); _paymentState = _$v.paymentState?.toBuilder(); _quoteState = _$v.quoteState?.toBuilder(); + _companyGatewayState = _$v.companyGatewayState?.toBuilder(); _groupState = _$v.groupState?.toBuilder(); _$v = null; } @@ -543,6 +571,7 @@ class UserCompanyStateBuilder projectState: projectState.build(), paymentState: paymentState.build(), quoteState: quoteState.build(), + companyGatewayState: companyGatewayState.build(), groupState: groupState.build()); } catch (_) { String _$failedField; @@ -571,6 +600,8 @@ class UserCompanyStateBuilder paymentState.build(); _$failedField = 'quoteState'; quoteState.build(); + _$failedField = 'companyGatewayState'; + companyGatewayState.build(); _$failedField = 'groupState'; groupState.build(); } catch (e) { diff --git a/lib/redux/company_gateway/company_gateway_actions.dart b/lib/redux/company_gateway/company_gateway_actions.dart new file mode 100644 index 000000000..b88b6c7c9 --- /dev/null +++ b/lib/redux/company_gateway/company_gateway_actions.dart @@ -0,0 +1,268 @@ +import 'dart:async'; +import 'package:flutter/widgets.dart'; +import 'package:built_collection/built_collection.dart'; +import 'package:invoiceninja_flutter/data/models/models.dart'; +import 'package:invoiceninja_flutter/redux/app/app_actions.dart'; +import 'package:flutter_redux/flutter_redux.dart'; +import 'package:invoiceninja_flutter/data/models/company_gateway_model.dart'; +import 'package:invoiceninja_flutter/redux/app/app_state.dart'; +import 'package:invoiceninja_flutter/utils/localization.dart'; +import 'package:invoiceninja_flutter/utils/completers.dart'; + +class ViewCompanyGatewayList implements PersistUI { + ViewCompanyGatewayList({@required this.context, this.force = false}); + + final BuildContext context; + final bool force; +} + +class ViewCompanyGateway implements PersistUI { + ViewCompanyGateway({ + @required this.companyGatewayId, + @required this.context, + this.force = false, + }); + + final String companyGatewayId; + final BuildContext context; + final bool force; +} + +class EditCompanyGateway implements PersistUI { + EditCompanyGateway( + {@required this.companyGateway, + @required this.context, + this.completer, + this.force = false}); + + final CompanyGatewayEntity companyGateway; + final BuildContext context; + final Completer completer; + final bool force; +} + +class UpdateCompanyGateway implements PersistUI { + UpdateCompanyGateway(this.companyGateway); + + final CompanyGatewayEntity companyGateway; +} + +class LoadCompanyGateway { + LoadCompanyGateway({this.completer, this.companyGatewayId, this.loadActivities = false}); + + final Completer completer; + final String companyGatewayId; + final bool loadActivities; +} + +class LoadCompanyGatewayActivity { + LoadCompanyGatewayActivity({this.completer, this.companyGatewayId}); + + final Completer completer; + final String companyGatewayId; +} + +class LoadCompanyGateways { + LoadCompanyGateways({this.completer, this.force = false}); + + final Completer completer; + final bool force; +} + +class LoadCompanyGatewayRequest implements StartLoading {} + +class LoadCompanyGatewayFailure implements StopLoading { + LoadCompanyGatewayFailure(this.error); + + final dynamic error; + + @override + String toString() { + return 'LoadCompanyGatewayFailure{error: $error}'; + } +} + +class LoadCompanyGatewaySuccess implements StopLoading, PersistData { + LoadCompanyGatewaySuccess(this.companyGateway); + + final CompanyGatewayEntity companyGateway; + + @override + String toString() { + return 'LoadCompanyGatewaySuccess{companyGateway: $companyGateway}'; + } +} + +class LoadCompanyGatewaysRequest implements StartLoading {} + +class LoadCompanyGatewaysFailure implements StopLoading { + LoadCompanyGatewaysFailure(this.error); + + final dynamic error; + + @override + String toString() { + return 'LoadCompanyGatewaysFailure{error: $error}'; + } +} + +class LoadCompanyGatewaysSuccess implements StopLoading, PersistData { + LoadCompanyGatewaysSuccess(this.companyGateways); + + final BuiltList companyGateways; + + @override + String toString() { + return 'LoadCompanyGatewaysSuccess{companyGateways: $companyGateways}'; + } +} + + +class SaveCompanyGatewayRequest implements StartSaving { + SaveCompanyGatewayRequest({this.completer, this.companyGateway}); + + final Completer completer; + final CompanyGatewayEntity companyGateway; +} + +class SaveCompanyGatewaySuccess implements StopSaving, PersistData, PersistUI { + SaveCompanyGatewaySuccess(this.companyGateway); + + final CompanyGatewayEntity companyGateway; +} + +class AddCompanyGatewaySuccess implements StopSaving, PersistData, PersistUI { + AddCompanyGatewaySuccess(this.companyGateway); + + final CompanyGatewayEntity companyGateway; +} + +class SaveCompanyGatewayFailure implements StopSaving { + SaveCompanyGatewayFailure (this.error); + + final Object error; +} + +class ArchiveCompanyGatewayRequest implements StartSaving { + ArchiveCompanyGatewayRequest(this.completer, this.companyGatewayId); + + final Completer completer; + final String companyGatewayId; +} + +class ArchiveCompanyGatewaySuccess implements StopSaving, PersistData { + ArchiveCompanyGatewaySuccess(this.companyGateway); + + final CompanyGatewayEntity companyGateway; +} + +class ArchiveCompanyGatewayFailure implements StopSaving { + ArchiveCompanyGatewayFailure(this.companyGateway); + + final CompanyGatewayEntity companyGateway; +} + +class DeleteCompanyGatewayRequest implements StartSaving { + DeleteCompanyGatewayRequest(this.completer, this.companyGatewayId); + + final Completer completer; + final String companyGatewayId; +} + +class DeleteCompanyGatewaySuccess implements StopSaving, PersistData { + DeleteCompanyGatewaySuccess(this.companyGateway); + + final CompanyGatewayEntity companyGateway; +} + +class DeleteCompanyGatewayFailure implements StopSaving { + DeleteCompanyGatewayFailure(this.companyGateway); + + final CompanyGatewayEntity companyGateway; +} + +class RestoreCompanyGatewayRequest implements StartSaving { + RestoreCompanyGatewayRequest(this.completer, this.companyGatewayId); + + final Completer completer; + final String companyGatewayId; +} + +class RestoreCompanyGatewaySuccess implements StopSaving, PersistData { + RestoreCompanyGatewaySuccess(this.companyGateway); + + final CompanyGatewayEntity companyGateway; +} + +class RestoreCompanyGatewayFailure implements StopSaving { + RestoreCompanyGatewayFailure(this.companyGateway); + + final CompanyGatewayEntity companyGateway; +} + + + + +class FilterCompanyGateways { + FilterCompanyGateways(this.filter); + + final String filter; +} + +class SortCompanyGateways implements PersistUI { + SortCompanyGateways(this.field); + + final String field; +} + +class FilterCompanyGatewaysByState implements PersistUI { + FilterCompanyGatewaysByState(this.state); + + final EntityState state; +} + +class FilterCompanyGatewaysByCustom1 implements PersistUI { + FilterCompanyGatewaysByCustom1(this.value); + + final String value; +} + +class FilterCompanyGatewaysByCustom2 implements PersistUI { + FilterCompanyGatewaysByCustom2(this.value); + + final String value; +} + +class FilterCompanyGatewaysByEntity implements PersistUI { + FilterCompanyGatewaysByEntity({this.entityId, this.entityType}); + + final String entityId; + final EntityType entityType; +} + + +void handleCompanyGatewayAction( + BuildContext context, CompanyGatewayEntity companyGateway, EntityAction action) { + final store = StoreProvider.of(context); + final state = store.state; + final CompanyEntity company = state.selectedCompany; + final localization = AppLocalization.of(context); + + switch (action) { + case EntityAction.edit: + store.dispatch(EditCompanyGateway(context: context, companyGateway: companyGateway)); + break; + case EntityAction.restore: + store.dispatch(RestoreCompanyGatewayRequest( + snackBarCompleter(context, localization.restoredCompanyGateway), companyGateway.id)); + break; + case EntityAction.archive: + store.dispatch(ArchiveCompanyGatewayRequest( + snackBarCompleter(context, localization.archivedCompanyGateway), companyGateway.id)); + break; + case EntityAction.delete: + store.dispatch(DeleteCompanyGatewayRequest( + snackBarCompleter(context, localization.deletedCompanyGateway), companyGateway.id)); + break; + } +} \ No newline at end of file diff --git a/lib/redux/company_gateway/company_gateway_middleware.dart b/lib/redux/company_gateway/company_gateway_middleware.dart new file mode 100644 index 000000000..f9ba04dfc --- /dev/null +++ b/lib/redux/company_gateway/company_gateway_middleware.dart @@ -0,0 +1,281 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; +import 'package:redux/redux.dart'; +import 'package:invoiceninja_flutter/utils/platforms.dart'; +import 'package:invoiceninja_flutter/redux/app/app_middleware.dart'; +import 'package:invoiceninja_flutter/data/models/models.dart'; +import 'package:invoiceninja_flutter/redux/ui/ui_actions.dart'; +import 'package:invoiceninja_flutter/ui/company_gateway/company_gateway_screen.dart'; +import 'package:invoiceninja_flutter/ui/company_gateway/edit/company_gateway_edit_vm.dart'; +import 'package:invoiceninja_flutter/ui/company_gateway/view/company_gateway_view_vm.dart'; +import 'package:invoiceninja_flutter/redux/company_gateway/company_gateway_actions.dart'; +import 'package:invoiceninja_flutter/redux/app/app_state.dart'; +import 'package:invoiceninja_flutter/data/repositories/company_gateway_repository.dart'; + +List> createStoreCompanyGatewaysMiddleware([ + CompanyGatewayRepository repository = const CompanyGatewayRepository(), +]) { + final viewCompanyGatewayList = _viewCompanyGatewayList(); + final viewCompanyGateway = _viewCompanyGateway(); + final editCompanyGateway = _editCompanyGateway(); + final loadCompanyGateways = _loadCompanyGateways(repository); + final loadCompanyGateway = _loadCompanyGateway(repository); + final saveCompanyGateway = _saveCompanyGateway(repository); + final archiveCompanyGateway = _archiveCompanyGateway(repository); + final deleteCompanyGateway = _deleteCompanyGateway(repository); + final restoreCompanyGateway = _restoreCompanyGateway(repository); + + return [ + TypedMiddleware(viewCompanyGatewayList), + TypedMiddleware(viewCompanyGateway), + TypedMiddleware(editCompanyGateway), + TypedMiddleware(loadCompanyGateways), + TypedMiddleware(loadCompanyGateway), + TypedMiddleware(saveCompanyGateway), + TypedMiddleware(archiveCompanyGateway), + TypedMiddleware(deleteCompanyGateway), + TypedMiddleware(restoreCompanyGateway), + ]; +} + +Middleware _editCompanyGateway() { + return (Store store, dynamic dynamicAction, NextDispatcher next) async { + + final action = dynamicAction as EditCompanyGateway; + + if (!action.force && hasChanges( + store: store, context: action.context, action: action)) { + return; + } + + next(action); + + store.dispatch(UpdateCurrentRoute(CompanyGatewayEditScreen.route)); + + if (isMobile(action.context)) { + final companyGateway = + await Navigator.of(action.context).pushNamed(CompanyGatewayEditScreen.route); + + if (action.completer != null && companyGateway != null) { + action.completer.complete(companyGateway); + } + } + }; +} + +Middleware _viewCompanyGateway() { + return (Store store, dynamic dynamicAction, NextDispatcher next) async { + + final action = dynamicAction as ViewCompanyGateway; + + if (!action.force && hasChanges( + store: store, context: action.context, action: action)) { + return; + } + + + next(action); + + store.dispatch(UpdateCurrentRoute(CompanyGatewayViewScreen.route)); + + if (isMobile(action.context)) { + Navigator.of(action.context).pushNamed(CompanyGatewayViewScreen.route); + } + }; +} + +Middleware _viewCompanyGatewayList() { + return (Store store, dynamic dynamicAction, NextDispatcher next) { + final action = dynamicAction as ViewCompanyGatewayList; + + if (!action.force && hasChanges( + store: store, context: action.context, action: action)) { + return; + } + + next(action); + + if (store.state.companyGatewayState.isStale) { + store.dispatch(LoadCompanyGateways()); + } + + store.dispatch(UpdateCurrentRoute(CompanyGatewayScreen.route)); + + if (isMobile(action.context)) { + Navigator.of(action.context).pushNamedAndRemoveUntil( + CompanyGatewayScreen.route, (Route route) => false); + } + }; +} + +Middleware _archiveCompanyGateway(CompanyGatewayRepository repository) { + return (Store store, dynamic dynamicAction, NextDispatcher next) { + final action = dynamicAction as ArchiveCompanyGatewayRequest; + final origCompanyGateway = store.state.companyGatewayState.map[action.companyGatewayId]; + repository + .saveData(store.state.credentials, + origCompanyGateway, EntityAction.archive) + .then((CompanyGatewayEntity companyGateway) { + store.dispatch(ArchiveCompanyGatewaySuccess(companyGateway)); + if (action.completer != null) { + action.completer.complete(null); + } + }).catchError((Object error) { + print(error); + store.dispatch(ArchiveCompanyGatewayFailure(origCompanyGateway)); + if (action.completer != null) { + action.completer.completeError(error); + } + }); + + next(action); + }; +} + +Middleware _deleteCompanyGateway(CompanyGatewayRepository repository) { + return (Store store, dynamic dynamicAction, NextDispatcher next) { + final action = dynamicAction as DeleteCompanyGatewayRequest; + final origCompanyGateway = store.state.companyGatewayState.map[action.companyGatewayId]; + repository + .saveData(store.state.credentials, + origCompanyGateway, EntityAction.delete) + .then((CompanyGatewayEntity companyGateway) { + store.dispatch(DeleteCompanyGatewaySuccess(companyGateway)); + if (action.completer != null) { + action.completer.complete(null); + } + }).catchError((Object error) { + print(error); + store.dispatch(DeleteCompanyGatewayFailure(origCompanyGateway)); + if (action.completer != null) { + action.completer.completeError(error); + } + }); + + next(action); + }; +} + +Middleware _restoreCompanyGateway(CompanyGatewayRepository repository) { + return (Store store, dynamic dynamicAction, NextDispatcher next) { + final action = dynamicAction as RestoreCompanyGatewayRequest; + final origCompanyGateway = store.state.companyGatewayState.map[action.companyGatewayId]; + repository + .saveData(store.state.credentials, + origCompanyGateway, EntityAction.restore) + .then((CompanyGatewayEntity companyGateway) { + store.dispatch(RestoreCompanyGatewaySuccess(companyGateway)); + if (action.completer != null) { + action.completer.complete(null); + } + }).catchError((Object error) { + print(error); + store.dispatch(RestoreCompanyGatewayFailure(origCompanyGateway)); + if (action.completer != null) { + action.completer.completeError(error); + } + }); + + next(action); + }; +} + +Middleware _saveCompanyGateway(CompanyGatewayRepository repository) { + return (Store store, dynamic dynamicAction, NextDispatcher next) { + final action = dynamicAction as SaveCompanyGatewayRequest; + repository + .saveData( + store.state.credentials, action.companyGateway) + .then((CompanyGatewayEntity companyGateway) { + if (action.companyGateway.isNew) { + store.dispatch(AddCompanyGatewaySuccess(companyGateway)); + } else { + store.dispatch(SaveCompanyGatewaySuccess(companyGateway)); + } + final companyGatewayUIState = store.state.companyGatewayUIState; + if (companyGatewayUIState.saveCompleter != null) { + companyGatewayUIState.saveCompleter.complete(companyGateway); + } + }).catchError((Object error) { + print(error); + store.dispatch(SaveCompanyGatewayFailure(error)); + action.completer.completeError(error); + }); + + next(action); + }; +} + +Middleware _loadCompanyGateway(CompanyGatewayRepository repository) { + return (Store store, dynamic dynamicAction, NextDispatcher next) { + final action = dynamicAction as LoadCompanyGateway; + final AppState state = store.state; + + if (state.isLoading) { + next(action); + return; + } + + store.dispatch(LoadCompanyGatewayRequest()); + repository + .loadItem(state.credentials, action.companyGatewayId) + .then((companyGateway) { + store.dispatch(LoadCompanyGatewaySuccess(companyGateway)); + + if (action.completer != null) { + action.completer.complete(null); + } + }).catchError((Object error) { + print(error); + store.dispatch(LoadCompanyGatewayFailure(error)); + if (action.completer != null) { + action.completer.completeError(error); + } + }); + + next(action); + }; +} + +Middleware _loadCompanyGateways(CompanyGatewayRepository repository) { + return (Store store, dynamic dynamicAction, NextDispatcher next) { + final action = dynamicAction as LoadCompanyGateways; + final AppState state = store.state; + + if (!state.companyGatewayState.isStale && !action.force) { + next(action); + return; + } + + if (state.isLoading) { + next(action); + return; + } + + final int updatedAt = (state.companyGatewayState.lastUpdated / 1000).round(); + + store.dispatch(LoadCompanyGatewaysRequest()); + repository + .loadList(state.credentials, updatedAt) + .then((data) { + store.dispatch(LoadCompanyGatewaysSuccess(data)); + + if (action.completer != null) { + action.completer.complete(null); + } + /* + if (state.productState.isStale) { + store.dispatch(LoadProducts()); + } + */ + }).catchError((Object error) { + print(error); + store.dispatch(LoadCompanyGatewaysFailure(error)); + if (action.completer != null) { + action.completer.completeError(error); + } + }); + + next(action); + }; +} diff --git a/lib/redux/company_gateway/company_gateway_reducer.dart b/lib/redux/company_gateway/company_gateway_reducer.dart new file mode 100644 index 000000000..4b56f4c04 --- /dev/null +++ b/lib/redux/company_gateway/company_gateway_reducer.dart @@ -0,0 +1,205 @@ +import 'package:redux/redux.dart'; +import 'package:invoiceninja_flutter/redux/app/app_actions.dart'; +import 'package:invoiceninja_flutter/data/models/models.dart'; +import 'package:invoiceninja_flutter/redux/company/company_actions.dart'; +import 'package:invoiceninja_flutter/redux/ui/entity_ui_state.dart'; +import 'package:invoiceninja_flutter/redux/ui/list_ui_state.dart'; +import 'package:invoiceninja_flutter/redux/company_gateway/company_gateway_actions.dart'; +import 'package:invoiceninja_flutter/redux/company_gateway/company_gateway_state.dart'; + +EntityUIState companyGatewayUIReducer(CompanyGatewayUIState state, dynamic action) { + return state.rebuild((b) => b + ..listUIState.replace(companyGatewayListReducer(state.listUIState, action)) + ..editing.replace(editingReducer(state.editing, action)) + ..selectedId = selectedIdReducer(state.selectedId, action)); +} + +Reducer selectedIdReducer = combineReducers([ + TypedReducer( + (String selectedId, dynamic action) => action.companyGatewayId), + TypedReducer( + (String selectedId, dynamic action) => action.companyGateway.id), + TypedReducer((selectedId, action) => action.entityId == null ? selectedId : '') +]); + +final editingReducer = combineReducers([ + TypedReducer(_updateEditing), + TypedReducer(_updateEditing), + TypedReducer(_updateEditing), + TypedReducer(_updateEditing), + TypedReducer(_updateEditing), + TypedReducer(_updateEditing), + TypedReducer((companyGateway, action) { + return action.companyGateway.rebuild((b) => b..isChanged = true); + }), + TypedReducer(_clearEditing), + TypedReducer(_clearEditing), +]); + +CompanyGatewayEntity _clearEditing(CompanyGatewayEntity companyGateway, dynamic action) { + return CompanyGatewayEntity(); +} + +CompanyGatewayEntity _updateEditing(CompanyGatewayEntity companyGateway, dynamic action) { + return action.companyGateway; +} + + +final companyGatewayListReducer = combineReducers([ + TypedReducer(_sortCompanyGateways), + TypedReducer(_filterCompanyGatewaysByState), + TypedReducer(_filterCompanyGateways), + TypedReducer(_filterCompanyGatewaysByCustom1), + TypedReducer(_filterCompanyGatewaysByCustom2), + TypedReducer(_filterCompanyGatewaysByClient), +]); + +ListUIState _filterCompanyGatewaysByClient( + ListUIState companyGatewayListState, FilterCompanyGatewaysByEntity action) { + return companyGatewayListState.rebuild((b) => b + ..filterEntityId = action.entityId + ..filterEntityType = action.entityType); +} + +ListUIState _filterCompanyGatewaysByCustom1( + ListUIState companyGatewayListState, FilterCompanyGatewaysByCustom1 action) { + if (companyGatewayListState.custom1Filters.contains(action.value)) { + return companyGatewayListState + .rebuild((b) => b..custom1Filters.remove(action.value)); + } else { + return companyGatewayListState.rebuild((b) => b..custom1Filters.add(action.value)); + } +} + +ListUIState _filterCompanyGatewaysByCustom2( + ListUIState companyGatewayListState, FilterCompanyGatewaysByCustom2 action) { + if (companyGatewayListState.custom2Filters.contains(action.value)) { + return companyGatewayListState + .rebuild((b) => b..custom2Filters.remove(action.value)); + } else { + return companyGatewayListState.rebuild((b) => b..custom2Filters.add(action.value)); + } +} + +ListUIState _filterCompanyGatewaysByState( + ListUIState companyGatewayListState, FilterCompanyGatewaysByState action) { + if (companyGatewayListState.stateFilters.contains(action.state)) { + return companyGatewayListState.rebuild((b) => b..stateFilters.remove(action.state)); + } else { + return companyGatewayListState.rebuild((b) => b..stateFilters.add(action.state)); + } +} + +ListUIState _filterCompanyGateways(ListUIState companyGatewayListState, FilterCompanyGateways action) { + return companyGatewayListState.rebuild((b) => b..filter = action.filter + ..filterClearedAt = action.filter == null + ? DateTime.now().millisecondsSinceEpoch + : companyGatewayListState.filterClearedAt); +} + +ListUIState _sortCompanyGateways(ListUIState companyGatewayListState, SortCompanyGateways action) { + return companyGatewayListState.rebuild((b) => b + ..sortAscending = b.sortField != action.field || !b.sortAscending + ..sortField = action.field); +} + +final companyGatewaysReducer = combineReducers([ + TypedReducer(_updateCompanyGateway), + TypedReducer(_addCompanyGateway), + TypedReducer(_setLoadedCompanyGateways), + TypedReducer(_setLoadedCompanyGateway), + TypedReducer(_archiveCompanyGatewayRequest), + TypedReducer(_archiveCompanyGatewaySuccess), + TypedReducer(_archiveCompanyGatewayFailure), + TypedReducer(_deleteCompanyGatewayRequest), + TypedReducer(_deleteCompanyGatewaySuccess), + TypedReducer(_deleteCompanyGatewayFailure), + TypedReducer(_restoreCompanyGatewayRequest), + TypedReducer(_restoreCompanyGatewaySuccess), + TypedReducer(_restoreCompanyGatewayFailure), +]); + +CompanyGatewayState _archiveCompanyGatewayRequest( + CompanyGatewayState companyGatewayState, ArchiveCompanyGatewayRequest action) { + final companyGateway = companyGatewayState.map[action.companyGatewayId] + .rebuild((b) => b..archivedAt = DateTime.now().millisecondsSinceEpoch); + + return companyGatewayState.rebuild((b) => b..map[action.companyGatewayId] = companyGateway); +} + +CompanyGatewayState _archiveCompanyGatewaySuccess( + CompanyGatewayState companyGatewayState, ArchiveCompanyGatewaySuccess action) { + return companyGatewayState.rebuild((b) => b..map[action.companyGateway.id] = action.companyGateway); +} + +CompanyGatewayState _archiveCompanyGatewayFailure( + CompanyGatewayState companyGatewayState, ArchiveCompanyGatewayFailure action) { + return companyGatewayState.rebuild((b) => b..map[action.companyGateway.id] = action.companyGateway); +} + +CompanyGatewayState _deleteCompanyGatewayRequest( + CompanyGatewayState companyGatewayState, DeleteCompanyGatewayRequest action) { + final companyGateway = companyGatewayState.map[action.companyGatewayId].rebuild((b) => b + ..archivedAt = DateTime.now().millisecondsSinceEpoch + ..isDeleted = true); + + return companyGatewayState.rebuild((b) => b..map[action.companyGatewayId] = companyGateway); +} + +CompanyGatewayState _deleteCompanyGatewaySuccess( + CompanyGatewayState companyGatewayState, DeleteCompanyGatewaySuccess action) { + return companyGatewayState.rebuild((b) => b..map[action.companyGateway.id] = action.companyGateway); +} + +CompanyGatewayState _deleteCompanyGatewayFailure( + CompanyGatewayState companyGatewayState, DeleteCompanyGatewayFailure action) { + return companyGatewayState.rebuild((b) => b..map[action.companyGateway.id] = action.companyGateway); +} + +CompanyGatewayState _restoreCompanyGatewayRequest( + CompanyGatewayState companyGatewayState, RestoreCompanyGatewayRequest action) { + final companyGateway = companyGatewayState.map[action.companyGatewayId].rebuild((b) => b + ..archivedAt = null + ..isDeleted = false); + return companyGatewayState.rebuild((b) => b..map[action.companyGatewayId] = companyGateway); +} + +CompanyGatewayState _restoreCompanyGatewaySuccess( + CompanyGatewayState companyGatewayState, RestoreCompanyGatewaySuccess action) { + return companyGatewayState.rebuild((b) => b..map[action.companyGateway.id] = action.companyGateway); +} + +CompanyGatewayState _restoreCompanyGatewayFailure( + CompanyGatewayState companyGatewayState, RestoreCompanyGatewayFailure action) { + return companyGatewayState.rebuild((b) => b..map[action.companyGateway.id] = action.companyGateway); +} + +CompanyGatewayState _addCompanyGateway(CompanyGatewayState companyGatewayState, AddCompanyGatewaySuccess action) { + return companyGatewayState.rebuild((b) => b + ..map[action.companyGateway.id] = action.companyGateway + ..list.add(action.companyGateway.id)); +} + +CompanyGatewayState _updateCompanyGateway(CompanyGatewayState companyGatewayState, SaveCompanyGatewaySuccess action) { + return companyGatewayState.rebuild((b) => b + ..map[action.companyGateway.id] = action.companyGateway); +} + +CompanyGatewayState _setLoadedCompanyGateway( + CompanyGatewayState companyGatewayState, LoadCompanyGatewaySuccess action) { + return companyGatewayState.rebuild((b) => b + ..map[action.companyGateway.id] = action.companyGateway); +} + +CompanyGatewayState _setLoadedCompanyGateways( + CompanyGatewayState companyGatewayState, LoadCompanyGatewaysSuccess action) { + final state = companyGatewayState.rebuild((b) => b + ..lastUpdated = DateTime.now().millisecondsSinceEpoch + ..map.addAll(Map.fromIterable( + action.companyGateways, + key: (dynamic item) => item.id, + value: (dynamic item) => item, + ))); + + return state.rebuild((b) => b..list.replace(state.map.keys)); +} diff --git a/lib/redux/company_gateway/company_gateway_selectors.dart b/lib/redux/company_gateway/company_gateway_selectors.dart new file mode 100644 index 000000000..871b83130 --- /dev/null +++ b/lib/redux/company_gateway/company_gateway_selectors.dart @@ -0,0 +1,74 @@ +import 'package:invoiceninja_flutter/data/models/company_gateway_model.dart'; +import 'package:memoize/memoize.dart'; +import 'package:built_collection/built_collection.dart'; +import 'package:invoiceninja_flutter/data/models/models.dart'; +import 'package:invoiceninja_flutter/redux/ui/list_ui_state.dart'; + +var memoizedDropdownCompanyGatewayList = memo3( + (BuiltMap companyGatewayMap, BuiltList companyGatewayList, + String clientId) => + dropdownCompanyGatewaysSelector(companyGatewayMap, companyGatewayList, clientId)); + +List dropdownCompanyGatewaysSelector(BuiltMap companyGatewayMap, + BuiltList companyGatewayList, String clientId) { + final list = companyGatewayList.where((companyGatewayId) { + final companyGateway = companyGatewayMap[companyGatewayId]; + /* + if (clientId != null && clientId > 0 && companyGateway.clientId != clientId) { + return false; + } + */ + return companyGateway.isActive; + }).toList(); + + list.sort((companyGatewayAId, companyGatewayBId) { + final companyGatewayA = companyGatewayMap[companyGatewayAId]; + final companyGatewayB = companyGatewayMap[companyGatewayBId]; + return companyGatewayA.compareTo(companyGatewayB, CompanyGatewayFields.name, true); + }); + + return list; +} + +var memoizedFilteredCompanyGatewayList = memo3((BuiltMap companyGatewayMap, + BuiltList companyGatewayList, ListUIState companyGatewayListState) => + filteredCompanyGatewaysSelector(companyGatewayMap, companyGatewayList, companyGatewayListState)); + +List filteredCompanyGatewaysSelector(BuiltMap companyGatewayMap, + BuiltList companyGatewayList, ListUIState companyGatewayListState) { + final list = companyGatewayList.where((companyGatewayId) { + final companyGateway = companyGatewayMap[companyGatewayId]; + if (companyGatewayListState.filterEntityId != null && + companyGateway.entityId != companyGatewayListState.filterEntityId) { + return false; + } else { + + } + + if (!companyGateway.matchesStates(companyGatewayListState.stateFilters)) { + return false; + } + if (companyGatewayListState.custom1Filters.isNotEmpty && + !companyGatewayListState.custom1Filters.contains(companyGateway.customValue1)) { + return false; + } + if (companyGatewayListState.custom2Filters.isNotEmpty && + !companyGatewayListState.custom2Filters.contains(companyGateway.customValue2)) { + return false; + } + return companyGateway.matchesFilter(companyGatewayListState.filter); + }).toList(); + + list.sort((companyGatewayAId, companyGatewayBId) { + final companyGatewayA = companyGatewayMap[companyGatewayAId]; + final companyGatewayB = companyGatewayMap[companyGatewayBId]; + return companyGatewayA.compareTo( + companyGatewayB, companyGatewayListState.sortField, companyGatewayListState.sortAscending); + }); + + return list; +} + +bool hasCompanyGatewayChanges( + CompanyGatewayEntity companyGateway, BuiltMap companyGatewayMap) => + companyGateway.isNew ? companyGateway.isChanged : companyGateway != companyGatewayMap[companyGateway.id]; diff --git a/lib/redux/company_gateway/company_gateway_state.dart b/lib/redux/company_gateway/company_gateway_state.dart new file mode 100644 index 000000000..e20b0e371 --- /dev/null +++ b/lib/redux/company_gateway/company_gateway_state.dart @@ -0,0 +1,61 @@ +import 'dart:async'; +import 'package:built_value/built_value.dart'; +import 'package:built_value/serializer.dart'; +import 'package:built_collection/built_collection.dart'; +import 'package:invoiceninja_flutter/constants.dart'; +import 'package:invoiceninja_flutter/data/models/company_gateway_model.dart'; +import 'package:invoiceninja_flutter/redux/ui/entity_ui_state.dart'; +import 'package:invoiceninja_flutter/redux/ui/list_ui_state.dart'; +import 'package:invoiceninja_flutter/data/models/models.dart'; + +part 'company_gateway_state.g.dart'; + +abstract class CompanyGatewayState implements Built { + + factory CompanyGatewayState() { + return _$CompanyGatewayState._( + lastUpdated: 0, + map: BuiltMap(), + list: BuiltList(), + ); + } + CompanyGatewayState._(); + + @nullable + int get lastUpdated; + + BuiltMap get map; + BuiltList get list; + + bool get isStale { + if (! isLoaded) { + return true; + } + + return DateTime.now().millisecondsSinceEpoch - lastUpdated > kMillisecondsToRefreshData; + } + + bool get isLoaded => lastUpdated != null && lastUpdated > 0; + + static Serializer get serializer => _$companyGatewayStateSerializer; +} + +abstract class CompanyGatewayUIState extends Object with EntityUIState implements Built { + + factory CompanyGatewayUIState() { + return _$CompanyGatewayUIState._( + listUIState: ListUIState(CompanyGatewayFields.name), + editing: CompanyGatewayEntity(), + selectedId: '', + ); + } + CompanyGatewayUIState._(); + + @nullable + CompanyGatewayEntity get editing; + + @override + bool get isCreatingNew => editing.isNew; + + static Serializer get serializer => _$companyGatewayUIStateSerializer; +} \ No newline at end of file diff --git a/lib/redux/company_gateway/company_gateway_state.g.dart b/lib/redux/company_gateway/company_gateway_state.g.dart new file mode 100644 index 000000000..d8fc9a149 --- /dev/null +++ b/lib/redux/company_gateway/company_gateway_state.g.dart @@ -0,0 +1,429 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'company_gateway_state.dart'; + +// ************************************************************************** +// BuiltValueGenerator +// ************************************************************************** + +Serializer _$companyGatewayStateSerializer = + new _$CompanyGatewayStateSerializer(); +Serializer _$companyGatewayUIStateSerializer = + new _$CompanyGatewayUIStateSerializer(); + +class _$CompanyGatewayStateSerializer + implements StructuredSerializer { + @override + final Iterable types = const [ + CompanyGatewayState, + _$CompanyGatewayState + ]; + @override + final String wireName = 'CompanyGatewayState'; + + @override + Iterable serialize( + Serializers serializers, CompanyGatewayState object, + {FullType specifiedType = FullType.unspecified}) { + final result = [ + 'map', + serializers.serialize(object.map, + specifiedType: const FullType(BuiltMap, const [ + const FullType(String), + const FullType(CompanyGatewayEntity) + ])), + 'list', + serializers.serialize(object.list, + specifiedType: + const FullType(BuiltList, const [const FullType(String)])), + ]; + if (object.lastUpdated != null) { + result + ..add('lastUpdated') + ..add(serializers.serialize(object.lastUpdated, + specifiedType: const FullType(int))); + } + return result; + } + + @override + CompanyGatewayState deserialize( + Serializers serializers, Iterable serialized, + {FullType specifiedType = FullType.unspecified}) { + final result = new CompanyGatewayStateBuilder(); + + final iterator = serialized.iterator; + while (iterator.moveNext()) { + final key = iterator.current as String; + iterator.moveNext(); + final dynamic value = iterator.current; + switch (key) { + case 'lastUpdated': + result.lastUpdated = serializers.deserialize(value, + specifiedType: const FullType(int)) as int; + break; + case 'map': + result.map.replace(serializers.deserialize(value, + specifiedType: const FullType(BuiltMap, const [ + const FullType(String), + const FullType(CompanyGatewayEntity) + ])) as BuiltMap); + break; + case 'list': + result.list.replace(serializers.deserialize(value, + specifiedType: + const FullType(BuiltList, const [const FullType(String)])) + as BuiltList); + break; + } + } + + return result.build(); + } +} + +class _$CompanyGatewayUIStateSerializer + implements StructuredSerializer { + @override + final Iterable types = const [ + CompanyGatewayUIState, + _$CompanyGatewayUIState + ]; + @override + final String wireName = 'CompanyGatewayUIState'; + + @override + Iterable serialize( + Serializers serializers, CompanyGatewayUIState object, + {FullType specifiedType = FullType.unspecified}) { + final result = [ + 'listUIState', + serializers.serialize(object.listUIState, + specifiedType: const FullType(ListUIState)), + ]; + if (object.editing != null) { + result + ..add('editing') + ..add(serializers.serialize(object.editing, + specifiedType: const FullType(CompanyGatewayEntity))); + } + if (object.selectedId != null) { + result + ..add('selectedId') + ..add(serializers.serialize(object.selectedId, + specifiedType: const FullType(String))); + } + return result; + } + + @override + CompanyGatewayUIState deserialize( + Serializers serializers, Iterable serialized, + {FullType specifiedType = FullType.unspecified}) { + final result = new CompanyGatewayUIStateBuilder(); + + final iterator = serialized.iterator; + while (iterator.moveNext()) { + final key = iterator.current as String; + iterator.moveNext(); + final dynamic value = iterator.current; + switch (key) { + case 'editing': + result.editing.replace(serializers.deserialize(value, + specifiedType: const FullType(CompanyGatewayEntity)) + as CompanyGatewayEntity); + break; + case 'listUIState': + result.listUIState.replace(serializers.deserialize(value, + specifiedType: const FullType(ListUIState)) as ListUIState); + break; + case 'selectedId': + result.selectedId = serializers.deserialize(value, + specifiedType: const FullType(String)) as String; + break; + } + } + + return result.build(); + } +} + +class _$CompanyGatewayState extends CompanyGatewayState { + @override + final int lastUpdated; + @override + final BuiltMap map; + @override + final BuiltList list; + + factory _$CompanyGatewayState( + [void Function(CompanyGatewayStateBuilder) updates]) => + (new CompanyGatewayStateBuilder()..update(updates)).build(); + + _$CompanyGatewayState._({this.lastUpdated, this.map, this.list}) : super._() { + if (map == null) { + throw new BuiltValueNullFieldError('CompanyGatewayState', 'map'); + } + if (list == null) { + throw new BuiltValueNullFieldError('CompanyGatewayState', 'list'); + } + } + + @override + CompanyGatewayState rebuild( + void Function(CompanyGatewayStateBuilder) updates) => + (toBuilder()..update(updates)).build(); + + @override + CompanyGatewayStateBuilder toBuilder() => + new CompanyGatewayStateBuilder()..replace(this); + + @override + bool operator ==(Object other) { + if (identical(other, this)) return true; + return other is CompanyGatewayState && + lastUpdated == other.lastUpdated && + map == other.map && + list == other.list; + } + + @override + int get hashCode { + return $jf( + $jc($jc($jc(0, lastUpdated.hashCode), map.hashCode), list.hashCode)); + } + + @override + String toString() { + return (newBuiltValueToStringHelper('CompanyGatewayState') + ..add('lastUpdated', lastUpdated) + ..add('map', map) + ..add('list', list)) + .toString(); + } +} + +class CompanyGatewayStateBuilder + implements Builder { + _$CompanyGatewayState _$v; + + int _lastUpdated; + int get lastUpdated => _$this._lastUpdated; + set lastUpdated(int lastUpdated) => _$this._lastUpdated = lastUpdated; + + MapBuilder _map; + MapBuilder get map => + _$this._map ??= new MapBuilder(); + set map(MapBuilder map) => _$this._map = map; + + ListBuilder _list; + ListBuilder get list => _$this._list ??= new ListBuilder(); + set list(ListBuilder list) => _$this._list = list; + + CompanyGatewayStateBuilder(); + + CompanyGatewayStateBuilder get _$this { + if (_$v != null) { + _lastUpdated = _$v.lastUpdated; + _map = _$v.map?.toBuilder(); + _list = _$v.list?.toBuilder(); + _$v = null; + } + return this; + } + + @override + void replace(CompanyGatewayState other) { + if (other == null) { + throw new ArgumentError.notNull('other'); + } + _$v = other as _$CompanyGatewayState; + } + + @override + void update(void Function(CompanyGatewayStateBuilder) updates) { + if (updates != null) updates(this); + } + + @override + _$CompanyGatewayState build() { + _$CompanyGatewayState _$result; + try { + _$result = _$v ?? + new _$CompanyGatewayState._( + lastUpdated: lastUpdated, map: map.build(), list: list.build()); + } catch (_) { + String _$failedField; + try { + _$failedField = 'map'; + map.build(); + _$failedField = 'list'; + list.build(); + } catch (e) { + throw new BuiltValueNestedFieldError( + 'CompanyGatewayState', _$failedField, e.toString()); + } + rethrow; + } + replace(_$result); + return _$result; + } +} + +class _$CompanyGatewayUIState extends CompanyGatewayUIState { + @override + final CompanyGatewayEntity editing; + @override + final ListUIState listUIState; + @override + final String selectedId; + @override + final Completer saveCompleter; + @override + final Completer cancelCompleter; + + factory _$CompanyGatewayUIState( + [void Function(CompanyGatewayUIStateBuilder) updates]) => + (new CompanyGatewayUIStateBuilder()..update(updates)).build(); + + _$CompanyGatewayUIState._( + {this.editing, + this.listUIState, + this.selectedId, + this.saveCompleter, + this.cancelCompleter}) + : super._() { + if (listUIState == null) { + throw new BuiltValueNullFieldError( + 'CompanyGatewayUIState', 'listUIState'); + } + } + + @override + CompanyGatewayUIState rebuild( + void Function(CompanyGatewayUIStateBuilder) updates) => + (toBuilder()..update(updates)).build(); + + @override + CompanyGatewayUIStateBuilder toBuilder() => + new CompanyGatewayUIStateBuilder()..replace(this); + + @override + bool operator ==(Object other) { + if (identical(other, this)) return true; + return other is CompanyGatewayUIState && + editing == other.editing && + listUIState == other.listUIState && + selectedId == other.selectedId && + saveCompleter == other.saveCompleter && + cancelCompleter == other.cancelCompleter; + } + + @override + int get hashCode { + return $jf($jc( + $jc( + $jc($jc($jc(0, editing.hashCode), listUIState.hashCode), + selectedId.hashCode), + saveCompleter.hashCode), + cancelCompleter.hashCode)); + } + + @override + String toString() { + return (newBuiltValueToStringHelper('CompanyGatewayUIState') + ..add('editing', editing) + ..add('listUIState', listUIState) + ..add('selectedId', selectedId) + ..add('saveCompleter', saveCompleter) + ..add('cancelCompleter', cancelCompleter)) + .toString(); + } +} + +class CompanyGatewayUIStateBuilder + implements Builder { + _$CompanyGatewayUIState _$v; + + CompanyGatewayEntityBuilder _editing; + CompanyGatewayEntityBuilder get editing => + _$this._editing ??= new CompanyGatewayEntityBuilder(); + set editing(CompanyGatewayEntityBuilder editing) => _$this._editing = editing; + + ListUIStateBuilder _listUIState; + ListUIStateBuilder get listUIState => + _$this._listUIState ??= new ListUIStateBuilder(); + set listUIState(ListUIStateBuilder listUIState) => + _$this._listUIState = listUIState; + + String _selectedId; + String get selectedId => _$this._selectedId; + set selectedId(String selectedId) => _$this._selectedId = selectedId; + + Completer _saveCompleter; + Completer get saveCompleter => _$this._saveCompleter; + set saveCompleter(Completer saveCompleter) => + _$this._saveCompleter = saveCompleter; + + Completer _cancelCompleter; + Completer get cancelCompleter => _$this._cancelCompleter; + set cancelCompleter(Completer cancelCompleter) => + _$this._cancelCompleter = cancelCompleter; + + CompanyGatewayUIStateBuilder(); + + CompanyGatewayUIStateBuilder get _$this { + if (_$v != null) { + _editing = _$v.editing?.toBuilder(); + _listUIState = _$v.listUIState?.toBuilder(); + _selectedId = _$v.selectedId; + _saveCompleter = _$v.saveCompleter; + _cancelCompleter = _$v.cancelCompleter; + _$v = null; + } + return this; + } + + @override + void replace(CompanyGatewayUIState other) { + if (other == null) { + throw new ArgumentError.notNull('other'); + } + _$v = other as _$CompanyGatewayUIState; + } + + @override + void update(void Function(CompanyGatewayUIStateBuilder) updates) { + if (updates != null) updates(this); + } + + @override + _$CompanyGatewayUIState build() { + _$CompanyGatewayUIState _$result; + try { + _$result = _$v ?? + new _$CompanyGatewayUIState._( + editing: _editing?.build(), + listUIState: listUIState.build(), + selectedId: selectedId, + saveCompleter: saveCompleter, + cancelCompleter: cancelCompleter); + } catch (_) { + String _$failedField; + try { + _$failedField = 'editing'; + _editing?.build(); + _$failedField = 'listUIState'; + listUIState.build(); + } catch (e) { + throw new BuiltValueNestedFieldError( + 'CompanyGatewayUIState', _$failedField, e.toString()); + } + rethrow; + } + replace(_$result); + return _$result; + } +} + +// ignore_for_file: always_put_control_body_on_new_line,always_specify_types,annotate_overrides,avoid_annotating_with_dynamic,avoid_as,avoid_catches_without_on_clauses,avoid_returning_this,lines_longer_than_80_chars,omit_local_variable_types,prefer_expression_function_bodies,sort_constructors_first,test_types_in_equals,unnecessary_const,unnecessary_new diff --git a/lib/redux/ui/ui_reducer.dart b/lib/redux/ui/ui_reducer.dart index 74ae27ac4..f0710ea3e 100644 --- a/lib/redux/ui/ui_reducer.dart +++ b/lib/redux/ui/ui_reducer.dart @@ -20,6 +20,8 @@ import 'package:invoiceninja_flutter/redux/quote/quote_reducer.dart'; import 'package:invoiceninja_flutter/redux/task/task_reducer.dart'; import 'package:invoiceninja_flutter/redux/vendor/vendor_reducer.dart'; // STARTER: import - do not remove comment +import 'package:invoiceninja_flutter/redux/company_gateway/company_gateway_reducer.dart'; + import 'package:invoiceninja_flutter/redux/group/group_reducer.dart'; UIState uiReducer(UIState state, dynamic action) { @@ -52,6 +54,8 @@ UIState uiReducer(UIState state, dynamic action) { ..dashboardUIState .replace(dashboardUIReducer(state.dashboardUIState, action)) // STARTER: reducer - do not remove comment +..companyGatewayUIState.replace(companyGatewayUIReducer(state.companyGatewayUIState, action)) + ..groupUIState.replace(groupUIReducer(state.groupUIState, action)) ..documentUIState.replace(documentUIReducer(state.documentUIState, action)) ..expenseUIState.replace(expenseUIReducer(state.expenseUIState, action)) diff --git a/lib/redux/ui/ui_state.dart b/lib/redux/ui/ui_state.dart index 78c3d1c8f..0f3e7996e 100644 --- a/lib/redux/ui/ui_state.dart +++ b/lib/redux/ui/ui_state.dart @@ -16,6 +16,8 @@ import 'package:invoiceninja_flutter/redux/quote/quote_state.dart'; import 'package:invoiceninja_flutter/redux/task/task_state.dart'; import 'package:invoiceninja_flutter/redux/vendor/vendor_state.dart'; // STARTER: import - do not remove comment +import 'package:invoiceninja_flutter/redux/company_gateway/company_gateway_state.dart'; + import 'package:invoiceninja_flutter/redux/group/group_state.dart'; part 'ui_state.g.dart'; @@ -49,6 +51,8 @@ abstract class UIState implements Built { clientUIState: ClientUIState(), invoiceUIState: InvoiceUIState(), // STARTER: constructor - do not remove comment +companyGatewayUIState: CompanyGatewayUIState(), + groupUIState: GroupUIState(), documentUIState: DocumentUIState(), @@ -102,6 +106,8 @@ abstract class UIState implements Built { InvoiceUIState get invoiceUIState; // STARTER: properties - do not remove comment +CompanyGatewayUIState get companyGatewayUIState; + GroupUIState get groupUIState; DocumentUIState get documentUIState; diff --git a/lib/redux/ui/ui_state.g.dart b/lib/redux/ui/ui_state.g.dart index 6a4ed8b37..17f5d692e 100644 --- a/lib/redux/ui/ui_state.g.dart +++ b/lib/redux/ui/ui_state.g.dart @@ -114,6 +114,9 @@ class _$UIStateSerializer implements StructuredSerializer { 'invoiceUIState', serializers.serialize(object.invoiceUIState, specifiedType: const FullType(InvoiceUIState)), + 'companyGatewayUIState', + serializers.serialize(object.companyGatewayUIState, + specifiedType: const FullType(CompanyGatewayUIState)), 'groupUIState', serializers.serialize(object.groupUIState, specifiedType: const FullType(GroupUIState)), @@ -235,6 +238,11 @@ class _$UIStateSerializer implements StructuredSerializer { result.invoiceUIState.replace(serializers.deserialize(value, specifiedType: const FullType(InvoiceUIState)) as InvoiceUIState); break; + case 'companyGatewayUIState': + result.companyGatewayUIState.replace(serializers.deserialize(value, + specifiedType: const FullType(CompanyGatewayUIState)) + as CompanyGatewayUIState); + break; case 'groupUIState': result.groupUIState.replace(serializers.deserialize(value, specifiedType: const FullType(GroupUIState)) as GroupUIState); @@ -352,6 +360,8 @@ class _$UIState extends UIState { @override final InvoiceUIState invoiceUIState; @override + final CompanyGatewayUIState companyGatewayUIState; + @override final GroupUIState groupUIState; @override final DocumentUIState documentUIState; @@ -392,6 +402,7 @@ class _$UIState extends UIState { this.productUIState, this.clientUIState, this.invoiceUIState, + this.companyGatewayUIState, this.groupUIState, this.documentUIState, this.expenseUIState, @@ -454,6 +465,9 @@ class _$UIState extends UIState { if (invoiceUIState == null) { throw new BuiltValueNullFieldError('UIState', 'invoiceUIState'); } + if (companyGatewayUIState == null) { + throw new BuiltValueNullFieldError('UIState', 'companyGatewayUIState'); + } if (groupUIState == null) { throw new BuiltValueNullFieldError('UIState', 'groupUIState'); } @@ -512,6 +526,7 @@ class _$UIState extends UIState { productUIState == other.productUIState && clientUIState == other.clientUIState && invoiceUIState == other.invoiceUIState && + companyGatewayUIState == other.companyGatewayUIState && groupUIState == other.groupUIState && documentUIState == other.documentUIState && expenseUIState == other.expenseUIState && @@ -543,17 +558,17 @@ class _$UIState extends UIState { $jc( $jc( $jc( - $jc($jc($jc($jc($jc($jc($jc($jc($jc(0, layout.hashCode), isTesting.hashCode), isMenuVisible.hashCode), isHistoryVisible.hashCode), selectedCompanyIndex.hashCode), currentRoute.hashCode), previousRoute.hashCode), enableDarkMode.hashCode), - longPressSelectionIsDefault.hashCode), - requireAuthentication.hashCode), - emailPayment.hashCode), - autoStartTasks.hashCode), - addDocumentsToInvoice.hashCode), - filter.hashCode), - dashboardUIState.hashCode), - productUIState.hashCode), - clientUIState.hashCode), - invoiceUIState.hashCode), + $jc($jc($jc($jc($jc($jc($jc($jc($jc($jc(0, layout.hashCode), isTesting.hashCode), isMenuVisible.hashCode), isHistoryVisible.hashCode), selectedCompanyIndex.hashCode), currentRoute.hashCode), previousRoute.hashCode), enableDarkMode.hashCode), longPressSelectionIsDefault.hashCode), + requireAuthentication.hashCode), + emailPayment.hashCode), + autoStartTasks.hashCode), + addDocumentsToInvoice.hashCode), + filter.hashCode), + dashboardUIState.hashCode), + productUIState.hashCode), + clientUIState.hashCode), + invoiceUIState.hashCode), + companyGatewayUIState.hashCode), groupUIState.hashCode), documentUIState.hashCode), expenseUIState.hashCode), @@ -586,6 +601,7 @@ class _$UIState extends UIState { ..add('productUIState', productUIState) ..add('clientUIState', clientUIState) ..add('invoiceUIState', invoiceUIState) + ..add('companyGatewayUIState', companyGatewayUIState) ..add('groupUIState', groupUIState) ..add('documentUIState', documentUIState) ..add('expenseUIState', expenseUIState) @@ -691,6 +707,13 @@ class UIStateBuilder implements Builder { set invoiceUIState(InvoiceUIStateBuilder invoiceUIState) => _$this._invoiceUIState = invoiceUIState; + CompanyGatewayUIStateBuilder _companyGatewayUIState; + CompanyGatewayUIStateBuilder get companyGatewayUIState => + _$this._companyGatewayUIState ??= new CompanyGatewayUIStateBuilder(); + set companyGatewayUIState( + CompanyGatewayUIStateBuilder companyGatewayUIState) => + _$this._companyGatewayUIState = companyGatewayUIState; + GroupUIStateBuilder _groupUIState; GroupUIStateBuilder get groupUIState => _$this._groupUIState ??= new GroupUIStateBuilder(); @@ -767,6 +790,7 @@ class UIStateBuilder implements Builder { _productUIState = _$v.productUIState?.toBuilder(); _clientUIState = _$v.clientUIState?.toBuilder(); _invoiceUIState = _$v.invoiceUIState?.toBuilder(); + _companyGatewayUIState = _$v.companyGatewayUIState?.toBuilder(); _groupUIState = _$v.groupUIState?.toBuilder(); _documentUIState = _$v.documentUIState?.toBuilder(); _expenseUIState = _$v.expenseUIState?.toBuilder(); @@ -818,6 +842,7 @@ class UIStateBuilder implements Builder { productUIState: productUIState.build(), clientUIState: clientUIState.build(), invoiceUIState: invoiceUIState.build(), + companyGatewayUIState: companyGatewayUIState.build(), groupUIState: groupUIState.build(), documentUIState: documentUIState.build(), expenseUIState: expenseUIState.build(), @@ -838,6 +863,8 @@ class UIStateBuilder implements Builder { clientUIState.build(); _$failedField = 'invoiceUIState'; invoiceUIState.build(); + _$failedField = 'companyGatewayUIState'; + companyGatewayUIState.build(); _$failedField = 'groupUIState'; groupUIState.build(); _$failedField = 'documentUIState'; diff --git a/lib/ui/app/app_drawer.dart b/lib/ui/app/app_drawer.dart index 661d43f69..197a38d97 100644 --- a/lib/ui/app/app_drawer.dart +++ b/lib/ui/app/app_drawer.dart @@ -25,6 +25,8 @@ import 'package:invoiceninja_flutter/redux/payment/payment_actions.dart'; import 'package:invoiceninja_flutter/redux/quote/quote_actions.dart'; import 'package:url_launcher/url_launcher.dart'; // STARTER: import - do not remove comment +import 'package:invoiceninja_flutter/redux/company_gateway/company_gateway_actions.dart'; + class AppDrawer extends StatelessWidget { const AppDrawer({ @@ -317,6 +319,19 @@ class AppDrawer extends StatelessWidget { ), */ // STARTER: menu - do not remove comment +DrawerTile( +company: company, +entityType: EntityType.companyGateway, +icon: getEntityIcon(EntityType.companyGateway), +title: localization.companyGateways, +onTap: () => store.dispatch(ViewcompanyGatewayList(context)), +onCreateTap: () { +navigator.pop(); +store.dispatch(EditCompanyGateway( +companyGateway: CompanyGatewayEntity(), context: context)); +}, +), + DrawerTile( company: company, icon: FontAwesomeIcons.cog, diff --git a/lib/ui/company_gateway/company_gateway_list.dart b/lib/ui/company_gateway/company_gateway_list.dart new file mode 100644 index 000000000..6df289cb9 --- /dev/null +++ b/lib/ui/company_gateway/company_gateway_list.dart @@ -0,0 +1,166 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:invoiceninja_flutter/data/models/models.dart'; +import 'package:invoiceninja_flutter/ui/app/entities/entity_actions_dialog.dart'; +import 'package:invoiceninja_flutter/ui/app/lists/list_divider.dart'; +import 'package:invoiceninja_flutter/ui/app/loading_indicator.dart'; +import 'package:invoiceninja_flutter/ui/app/help_text.dart'; +import 'package:invoiceninja_flutter/ui/app/snackbar_row.dart'; +import 'package:invoiceninja_flutter/ui/company_gateway/company_gateway_list_item.dart'; +import 'package:invoiceninja_flutter/ui/company_gateway/company_gateway_list_vm.dart'; +import 'package:invoiceninja_flutter/utils/icons.dart'; +import 'package:invoiceninja_flutter/utils/localization.dart'; + +class CompanyGatewayList extends StatelessWidget { + const CompanyGatewayList({ + Key key, + @required this.viewModel, + }) : super(key: key); + + final CompanyGatewayListVM viewModel; + + @override + Widget build(BuildContext context) { + /* + final localization = AppLocalization.of(context); + final listState = viewModel.listState; + final filteredClientId = listState.filterEntityId; + final filteredClient = + filteredClientId != null ? viewModel.clientMap[filteredClientId] : null; + */ + + return Column( + children: [ + + Expanded( + child: !viewModel.isLoaded + ? (viewModel.isLoading ? LoadingIndicator() : SizedBox()) + : RefreshIndicator( + onRefresh: () => viewModel.onRefreshed(context), + child: viewModel.companyGatewayList.isEmpty + ? HelpText(AppLocalization.of(context).noRecordsFound) + : ListView.separated( + shrinkWrap: true, + separatorBuilder: (context, index) => ListDivider(), + itemCount: viewModel.companyGatewayList.length, + itemBuilder: (BuildContext context, index) { + final companyGatewayId = viewModel.companyGatewayList[index]; + final companyGateway = viewModel.companyGatewayMap[companyGatewayId]; + final userCompany = viewModel.userCompany; + + void showDialog() => showEntityActionsDialog( + userCompany: userCompany, + entity: companyGateway, + context: context, + onEntityAction: viewModel.onEntityAction); + + return CompanyGatewayListItem( + user: viewModel.userCompany.user, + filter: viewModel.filter, + companyGateway: companyGateway, + onTap: () => + viewModel.onCompanyGatewayTap(context, companyGateway), + onEntityAction: (EntityAction action) { + if (action == EntityAction.more) { + showDialog(); + } else { + viewModel.onEntityAction( + context, companyGateway, action); + } + }, + onLongPress: () => + showDialog(), + ); + }, + ), + ), + ), + + + /* + filteredClient != null + ? Material( + color: Colors.orangeAccent, + elevation: 6.0, + child: InkWell( + onTap: () => viewModel.onViewEntityFilterPressed(context), + child: Row( + children: [ + SizedBox(width: 18.0), + Expanded( + child: Text( + '${localization.filteredBy} ${filteredClient.listDisplayName}', + style: TextStyle( + color: Colors.white, + fontSize: 16.0, + ), + ), + ), + IconButton( + icon: Icon( + Icons.close, + color: Colors.white, + ), + onPressed: () => viewModel.onClearEntityFilterPressed(), + ) + ], + ), + ), + ) + : Container(), + Expanded( + child: !viewModel.isLoaded + ? LoadingIndicator() + : RefreshIndicator( + onRefresh: () => viewModel.onRefreshed(context), + child: viewModel.companyGatewayList.isEmpty + ? HelpText(AppLocalization.of(context).noRecordsFound) + : ListView.separated( + shrinkWrap: true, + separatorBuilder: (context, index) => ListDivider(), + itemCount: viewModel.companyGatewayList.length, + itemBuilder: (BuildContext context, index) { + final user = viewModel.user; + final companyGatewayId = viewModel.companyGatewayList[index]; + final companyGateway = viewModel.companyGatewayMap[companyGatewayId]; + final client = + viewModel.clientMap[companyGateway.clientId] ?? + ClientEntity(); + + + void showDialog() => showEntityActionsDialog( + entity: companyGateway, + context: context, + user: user, + onEntityAction: viewModel.onEntityAction); + + + + return CompanyGatewayListItem( + user: viewModel.user, + filter: viewModel.filter, + companyGateway: companyGateway, + client: + viewModel.clientMap[companyGateway.clientId] ?? + ClientEntity(), + onTap: () => + viewModel.onCompanyGatewayTap(context, companyGateway), + onEntityAction: (EntityAction action) { + if (action == EntityAction.more) { + showDialog(); + } else { + viewModel.onEntityAction( + context, companyGateway, action); + } + }, + onLongPress: () => + showDialog(), + ); + }, + ), + ), + ),*/ + ], + ); + } +} diff --git a/lib/ui/company_gateway/company_gateway_list_item.dart b/lib/ui/company_gateway/company_gateway_list_item.dart new file mode 100644 index 000000000..26f269a3f --- /dev/null +++ b/lib/ui/company_gateway/company_gateway_list_item.dart @@ -0,0 +1,97 @@ +import 'package:flutter_redux/flutter_redux.dart'; +import 'package:invoiceninja_flutter/data/models/company_gateway_model.dart'; +import 'package:invoiceninja_flutter/redux/app/app_state.dart'; +import 'package:invoiceninja_flutter/ui/app/entity_state_label.dart'; +import 'package:invoiceninja_flutter/utils/formatting.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:invoiceninja_flutter/data/models/models.dart'; +import 'package:invoiceninja_flutter/ui/app/dismissible_entity.dart'; + +class CompanyGatewayListItem extends StatelessWidget { + + const CompanyGatewayListItem({ + @required this.user, + @required this.onEntityAction, + @required this.onTap, + @required this.onLongPress, + //@required this.onCheckboxChanged, + @required this.companyGateway, + @required this.filter, + }); + + final UserEntity user; + final Function(EntityAction) onEntityAction; + final GestureTapCallback onTap; + final GestureTapCallback onLongPress; + //final ValueChanged onCheckboxChanged; + final CompanyGatewayEntity companyGateway; + final String filter; + + static final companyGatewayItemKey = (int id) => Key('__company_gateway_item_${id}__'); + + @override + Widget build(BuildContext context) { + final store = StoreProvider.of(context); + final state = store.state; + final uiState = state.uiState; + final companyGatewayUIState = uiState.companyGatewayUIState; + + final filterMatch = filter != null && filter.isNotEmpty + ? companyGateway.matchesFilterValue(filter) + : null; + final subtitle = filterMatch; + + return DismissibleEntity( + userCompany: state.userCompany, + entity: companyGateway, + isSelected: companyGateway.id == + (uiState.isEditing + ? companyGatewayUIState.editing.id + : companyGatewayUIState.selectedId), + onEntityAction: onEntityAction, + child: ListTile( + onTap: onTap, + onLongPress: onLongPress, + /* + leading: Checkbox( + //key: NinjaKeys.companyGatewayItemCheckbox(companyGateway.id), + value: true, + //onChanged: onCheckboxChanged, + onChanged: (value) { + return true; + }, + ), + */ + title: Container( + width: MediaQuery.of(context).size.width, + child: Row( + children: [ + Expanded( + child: Text( + companyGateway.name, + //key: NinjaKeys.clientItemClientKey(client.id), + style: Theme.of(context).textTheme.title, + ), + ), + Text(formatNumber(companyGateway.listDisplayAmount, context), + style: Theme.of(context).textTheme.title), + ], + ), + ), + subtitle: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + subtitle != null && subtitle.isNotEmpty ? + Text( + subtitle, + maxLines: 3, + overflow: TextOverflow.ellipsis, + ) : Container(), + EntityStateLabel(companyGateway), + ], + ), + ), + ); + } +} diff --git a/lib/ui/company_gateway/company_gateway_list_vm.dart b/lib/ui/company_gateway/company_gateway_list_vm.dart new file mode 100644 index 000000000..3c42e13a5 --- /dev/null +++ b/lib/ui/company_gateway/company_gateway_list_vm.dart @@ -0,0 +1,102 @@ +import 'dart:async'; +import 'package:invoiceninja_flutter/data/models/company_gateway_model.dart'; +import 'package:redux/redux.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_redux/flutter_redux.dart'; +import 'package:built_collection/built_collection.dart'; +import 'package:invoiceninja_flutter/redux/client/client_actions.dart'; +import 'package:invoiceninja_flutter/redux/ui/list_ui_state.dart'; +import 'package:invoiceninja_flutter/utils/completers.dart'; +import 'package:invoiceninja_flutter/utils/localization.dart'; +import 'package:invoiceninja_flutter/redux/company_gateway/company_gateway_selectors.dart'; +import 'package:invoiceninja_flutter/data/models/models.dart'; +import 'package:invoiceninja_flutter/ui/company_gateway/company_gateway_list.dart'; +import 'package:invoiceninja_flutter/redux/app/app_state.dart'; +import 'package:invoiceninja_flutter/redux/company_gateway/company_gateway_actions.dart'; + +class CompanyGatewayListBuilder extends StatelessWidget { + const CompanyGatewayListBuilder({Key key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return StoreConnector( + converter: CompanyGatewayListVM.fromStore, + builder: (context, viewModel) { + return CompanyGatewayList( + viewModel: viewModel, + ); + }, + ); + } +} + +class CompanyGatewayListVM { + CompanyGatewayListVM({ + @required this.userCompany, + @required this.companyGatewayList, + @required this.companyGatewayMap, + @required this.filter, + @required this.isLoading, + @required this.isLoaded, + @required this.onCompanyGatewayTap, + @required this.listState, + @required this.onRefreshed, + @required this.onEntityAction, + @required this.onClearEntityFilterPressed, + @required this.onViewEntityFilterPressed, + }); + + static CompanyGatewayListVM fromStore(Store store) { + Future _handleRefresh(BuildContext context) { + if (store.state.isLoading) { + return Future(null); + } + final completer = snackBarCompleter( + context, AppLocalization.of(context).refreshComplete); + store.dispatch(LoadCompanyGateways(completer: completer, force: true)); + return completer.future; + } + + final state = store.state; + + return CompanyGatewayListVM( + userCompany: state.userCompany, + listState: state.companyGatewayListState, + companyGatewayList: memoizedFilteredCompanyGatewayList(state.companyGatewayState.map, + state.companyGatewayState.list, state.companyGatewayListState), + companyGatewayMap: state.companyGatewayState.map, + isLoading: state.isLoading, + isLoaded: state.companyGatewayState.isLoaded, + filter: state.companyGatewayUIState.listUIState.filter, + onClearEntityFilterPressed: () => + store.dispatch(FilterCompanyGatewaysByEntity()), + onViewEntityFilterPressed: (BuildContext context) => store.dispatch( + ViewClient( + clientId: state.companyGatewayListState.filterEntityId, + context: context)), + onCompanyGatewayTap: (context, companyGateway) { + store.dispatch(ViewCompanyGateway(companyGatewayId: companyGateway.id, context: context)); + }, + onEntityAction: + (BuildContext context, BaseEntity companyGateway, EntityAction action) => + handleCompanyGatewayAction(context, companyGateway, action), + onRefreshed: (context) => _handleRefresh(context), + ); + } + + final UserCompanyEntity userCompany; + final List companyGatewayList; + final BuiltMap companyGatewayMap; + final ListUIState listState; + final String filter; + final bool isLoading; + final bool isLoaded; + final Function(BuildContext, CompanyGatewayEntity) onCompanyGatewayTap; + final Function(BuildContext) onRefreshed; + final Function(BuildContext, CompanyGatewayEntity, EntityAction) onEntityAction; + final Function onClearEntityFilterPressed; + final Function(BuildContext) onViewEntityFilterPressed; + +} diff --git a/lib/ui/company_gateway/company_gateway_screen.dart b/lib/ui/company_gateway/company_gateway_screen.dart new file mode 100644 index 000000000..fe1e50cdf --- /dev/null +++ b/lib/ui/company_gateway/company_gateway_screen.dart @@ -0,0 +1,78 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_redux/flutter_redux.dart'; +import 'package:invoiceninja_flutter/ui/app/app_scaffold.dart'; +import 'package:invoiceninja_flutter/redux/dashboard/dashboard_actions.dart'; +import 'package:invoiceninja_flutter/data/models/company_gateway_model.dart'; +import 'package:invoiceninja_flutter/ui/app/list_filter.dart'; +import 'package:invoiceninja_flutter/ui/app/list_filter_button.dart'; +import 'package:invoiceninja_flutter/utils/localization.dart'; +import 'package:invoiceninja_flutter/redux/app/app_state.dart'; +import 'package:invoiceninja_flutter/data/models/models.dart'; +import 'package:invoiceninja_flutter/ui/company_gateway/company_gateway_list_vm.dart'; +import 'package:invoiceninja_flutter/redux/company_gateway/company_gateway_actions.dart'; +import 'package:invoiceninja_flutter/ui/app/app_drawer_vm.dart'; +import 'package:invoiceninja_flutter/ui/app/app_bottom_bar.dart'; + +class CompanyGatewayScreen extends StatelessWidget { + static const String route = '/company_gateway'; + + @override + Widget build(BuildContext context) { + final store = StoreProvider.of(context); + final state = store.state; + final company = state.selectedCompany; + final localization = AppLocalization.of(context); + + return AppScaffold( + appBarTitle: ListFilter( + key: ValueKey(state.companyGatewayListState.filterClearedAt), + entityType: EntityType.companyGateway, + onFilterChanged: (value) { + store.dispatch(FilterCompanyGateways(value)); + }, + ), + appBarActions: [ + ListFilterButton( + entityType: EntityType.companyGateway, + onFilterPressed: (String value) { + store.dispatch(FilterCompanyGateways(value)); + }, + ), + ], + body: CompanyGatewayListBuilder(), + bottomNavigationBar: AppBottomBar( + entityType: EntityType.companyGateway, + onSelectedSortField: (value) => store.dispatch(SortCompanyGateways(value)), + customValues1: company.getCustomFieldValues(CustomFieldType.companyGateway1, + excludeBlank: true), + customValues2: company.getCustomFieldValues(CustomFieldType.companyGateway2, + excludeBlank: true), + onSelectedCustom1: (value) => + store.dispatch(FilterCompanyGatewaysByCustom1(value)), + onSelectedCustom2: (value) => + store.dispatch(FilterCompanyGatewaysByCustom2(value)), + sortFields: [ + CompanyGatewayFields.updatedAt, + ], + onSelectedState: (EntityState state, value) { + store.dispatch(FilterCompanyGatewaysByState(state)); + }, + ), + floatingActionButton: user.canCreate(EntityType.companyGateway) + ? FloatingActionButton( + heroTag: 'company_gateway_fab', + backgroundColor: Theme.of(context).primaryColorDark, + onPressed: () { + store.dispatch( + EditCompanyGateway(companyGateway: CompanyGatewayEntity(), context: context)); + }, + child: Icon( + Icons.add, + color: Colors.white, + ), + tooltip: localization.newCompanyGateway, + ) + : null, + ); + } +} diff --git a/lib/ui/company_gateway/edit/company_gateway_edit.dart b/lib/ui/company_gateway/edit/company_gateway_edit.dart new file mode 100644 index 000000000..3511850c8 --- /dev/null +++ b/lib/ui/company_gateway/edit/company_gateway_edit.dart @@ -0,0 +1,122 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:invoiceninja_flutter/ui/app/form_card.dart'; +import 'package:invoiceninja_flutter/ui/company_gateway/edit/company_gateway_edit_vm.dart'; +import 'package:invoiceninja_flutter/ui/app/buttons/action_icon_button.dart'; +import 'package:invoiceninja_flutter/utils/localization.dart'; +import 'package:invoiceninja_flutter/utils/platforms.dart'; + +class CompanyGatewayEdit extends StatefulWidget { + const CompanyGatewayEdit({ + Key key, + @required this.viewModel, + }) : super(key: key); + + final CompanyGatewayEditVM viewModel; + + @override + _CompanyGatewayEditState createState() => _CompanyGatewayEditState(); +} + +class _CompanyGatewayEditState extends State { + static final GlobalKey _formKey = GlobalKey(); + + // STARTER: controllers - do not remove comment + + List _controllers = []; + + @override + void didChangeDependencies() { + + _controllers = [ + // STARTER: array - do not remove comment + ]; + + _controllers.forEach((controller) => controller.removeListener(_onChanged)); + + final companyGateway = widget.viewModel.companyGateway; + // STARTER: read value - do not remove comment + + _controllers.forEach((controller) => controller.addListener(_onChanged)); + + super.didChangeDependencies(); + } + + @override + void dispose() { + _controllers.forEach((controller) { + controller.removeListener(_onChanged); + controller.dispose(); + }); + + super.dispose(); + } + + void _onChanged() { + final companyGateway = widget.viewModel.companyGateway.rebuild((b) => b + // STARTER: set value - do not remove comment + ); + if (companyGateway != widget.viewModel.companyGateway) { + widget.viewModel.onChanged(companyGateway); + } + } + + @override + Widget build(BuildContext context) { + final viewModel = widget.viewModel; + final localization = AppLocalization.of(context); + final companyGateway = viewModel.companyGateway; + + return WillPopScope( + onWillPop: () async { + viewModel.onBackPressed(); + return true; + }, + child: Scaffold( + appBar: AppBar( + automaticallyImplyLeading: isMobile(context), + title: Text(viewModel.companyGateway.isNew + ? localization.newCompanyGateway + : localization.editCompanyGateway), + actions: [ + if (!isMobile(context)) + FlatButton( + child: Text( + localization.cancel, + style: TextStyle(color: Colors.white), + ), + onPressed: () => viewModel.onCancelPressed(context), + ), + ActionIconButton( + icon: Icons.cloud_upload, + tooltip: localization.save, + isVisible: !companyGateway.isDeleted, + isDirty: companyGateway.isNew || companyGateway != viewModel.origCompanyGateway, + isSaving: viewModel.isSaving, + onPressed: () { + if (! _formKey.currentState.validate()) { + return; + } + viewModel.onSavePressed(context); + }, + ), + ], + ), + body: Form( + key: _formKey, + child: Builder(builder: (BuildContext context) { + return ListView( + children: [ + FormCard( + children: [ + // STARTER: widgets - do not remove comment + ], + ), + ], + ); + }) + ), + ), + ); + } +} diff --git a/lib/ui/company_gateway/edit/company_gateway_edit_vm.dart b/lib/ui/company_gateway/edit/company_gateway_edit_vm.dart new file mode 100644 index 000000000..af03baff4 --- /dev/null +++ b/lib/ui/company_gateway/edit/company_gateway_edit_vm.dart @@ -0,0 +1,108 @@ +import 'dart:async'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_redux/flutter_redux.dart'; +import 'package:invoiceninja_flutter/redux/ui/ui_actions.dart'; +import 'package:invoiceninja_flutter/ui/company_gateway/company_gateway_screen.dart'; +import 'package:invoiceninja_flutter/utils/platforms.dart'; +import 'package:redux/redux.dart'; +import 'package:invoiceninja_flutter/data/models/models.dart'; +import 'package:invoiceninja_flutter/ui/app/dialogs/error_dialog.dart'; +import 'package:invoiceninja_flutter/ui/company_gateway/view/company_gateway_view_vm.dart'; +import 'package:invoiceninja_flutter/redux/company_gateway/company_gateway_actions.dart'; +import 'package:invoiceninja_flutter/data/models/company_gateway_model.dart'; +import 'package:invoiceninja_flutter/ui/company_gateway/edit/company_gateway_edit.dart'; +import 'package:invoiceninja_flutter/redux/app/app_state.dart'; + +class CompanyGatewayEditScreen extends StatelessWidget { + const CompanyGatewayEditScreen({Key key}) : super(key: key); + static const String route = '/company_gateway/edit'; + + @override + Widget build(BuildContext context) { + return StoreConnector( + converter: (Store store) { + return CompanyGatewayEditVM.fromStore(store); + }, + builder: (context, viewModel) { + return CompanyGatewayEdit( + viewModel: viewModel, + key: ValueKey(viewModel.companyGateway.id), + ); + }, + ); + } +} + +class CompanyGatewayEditVM { + CompanyGatewayEditVM({ + @required this.state, + @required this.companyGateway, + @required this.company, + @required this.onChanged, + @required this.isSaving, + @required this.origCompanyGateway, + @required this.onSavePressed, + @required this.onCancelPressed, + @required this.onBackPressed, + @required this.isLoading, + }); + + factory CompanyGatewayEditVM.fromStore(Store store) { + final companyGateway = store.state.companyGatewayUIState.editing; + final state = store.state; + + return CompanyGatewayEditVM( + state: state, + isLoading: state.isLoading, + isSaving: state.isSaving, + origCompanyGateway: state.companyGatewayState.map[companyGateway.id], + companyGateway: companyGateway, + company: state.selectedCompany, + onChanged: (CompanyGatewayEntity companyGateway) { + store.dispatch(UpdateCompanyGateway(companyGateway)); + }, + onBackPressed: () { + if (state.uiState.currentRoute.contains(CompanyGatewayScreen.route)) { + store.dispatch(UpdateCurrentRoute(companyGateway.isNew ? CompanyGatewayScreen.route : CompanyGatewayViewScreen.route)); + } + }, + onCancelPressed: (BuildContext context) { + store.dispatch(EditCompanyGateway( + companyGateway: CompanyGatewayEntity(), context: context, force: true)); + store.dispatch(UpdateCurrentRoute(state.uiState.previousRoute)); + }, + onSavePressed: (BuildContext context) { + final Completer completer = new Completer(); + store.dispatch(SaveCompanyGatewayRequest(completer: completer, companyGateway: companyGateway)); + return completer.future.then((savedCompanyGateway) { + store.dispatch(UpdateCurrentRoute(CompanyGatewayViewScreen.route)); + if (isMobile(context)) { + if (companyGateway.isNew) { + Navigator.of(context).pushReplacementNamed(CompanyGatewayViewScreen.route); + } else { + Navigator.of(context).pop(savedCompanyGateway); + } + } + }).catchError((Object error) { + showDialog( + context: context, + builder: (BuildContext context) { + return ErrorDialog(error); + }); + }); + }, + ); + } + + final CompanyGatewayEntity companyGateway; + final CompanyEntity company; + final Function(CompanyGatewayEntity) onChanged; + final Function(BuildContext) onSavePressed; + final Function(BuildContext) onCancelPressed; + final Function onBackPressed; + final bool isLoading; + final bool isSaving; + final CompanyGatewayEntity origCompanyGateway; + final AppState state; +} diff --git a/lib/ui/company_gateway/view/company_gateway_view.dart b/lib/ui/company_gateway/view/company_gateway_view.dart new file mode 100644 index 000000000..313a93674 --- /dev/null +++ b/lib/ui/company_gateway/view/company_gateway_view.dart @@ -0,0 +1,54 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:invoiceninja_flutter/ui/app/buttons/edit_icon_button.dart'; +import 'package:invoiceninja_flutter/ui/app/actions_menu_button.dart'; +import 'package:invoiceninja_flutter/ui/company_gateway/view/company_gateway_view_vm.dart'; +import 'package:invoiceninja_flutter/ui/app/form_card.dart'; +import 'package:invoiceninja_flutter/ui/app/entities/entity_state_title.dart'; + +class CompanyGatewayView extends StatefulWidget { + + const CompanyGatewayView({ + Key key, + @required this.viewModel, + }) : super(key: key); + + final CompanyGatewayViewVM viewModel; + + @override + _CompanyGatewayViewState createState() => new _CompanyGatewayViewState(); +} + +class _CompanyGatewayViewState extends State { + @override + Widget build(BuildContext context) { + final viewModel = widget.viewModel; + final userCompany = viewModel.state.userCompany; + final companyGateway = viewModel.companyGateway; + + return Scaffold( + appBar: AppBar( + title: EntityStateTitle(entity: companyGateway), + actions: [ + userCompany.canEditEntity(companyGateway) + ? EditIconButton( + isVisible: !companyGateway.isDeleted, + onPressed: () => viewModel.onEditPressed(context), + ) + : Container(), + ActionMenuButton( + entityActions: companyGateway.getActions(userCompany: userCompany), + isSaving: viewModel.isSaving, + entity: companyGateway, + onSelected: viewModel.onEntityAction, + ) + ], + ), + body: FormCard( + children: [ + // STARTER: widgets - do not remove comment + ] + ), + ); + } +} diff --git a/lib/ui/company_gateway/view/company_gateway_view_vm.dart b/lib/ui/company_gateway/view/company_gateway_view_vm.dart new file mode 100644 index 000000000..c16d1bd50 --- /dev/null +++ b/lib/ui/company_gateway/view/company_gateway_view_vm.dart @@ -0,0 +1,102 @@ +import 'dart:async'; +import 'package:invoiceninja_flutter/ui/app/snackbar_row.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:invoiceninja_flutter/redux/ui/ui_actions.dart'; +import 'package:invoiceninja_flutter/utils/completers.dart'; +import 'package:invoiceninja_flutter/ui/company_gateway/company_gateway_screen.dart'; +import 'package:invoiceninja_flutter/utils/localization.dart'; +import 'package:redux/redux.dart'; +import 'package:flutter_redux/flutter_redux.dart'; +import 'package:invoiceninja_flutter/redux/company_gateway/company_gateway_actions.dart'; +import 'package:invoiceninja_flutter/data/models/company_gateway_model.dart'; +import 'package:invoiceninja_flutter/data/models/models.dart'; +import 'package:invoiceninja_flutter/ui/company_gateway/view/company_gateway_view.dart'; +import 'package:invoiceninja_flutter/redux/app/app_state.dart'; + +class CompanyGatewayViewScreen extends StatelessWidget { + const CompanyGatewayViewScreen({Key key}) : super(key: key); + static const String route = '/company_gateway/view'; + + @override + Widget build(BuildContext context) { + return StoreConnector( + converter: (Store store) { + return CompanyGatewayViewVM.fromStore(store); + }, + builder: (context, vm) { + return CompanyGatewayView( + viewModel: vm, + ); + }, + ); + } +} + +class CompanyGatewayViewVM { + + CompanyGatewayViewVM({ + @required this.state, + @required this.companyGateway, + @required this.company, + @required this.onEntityAction, + @required this.onEditPressed, + @required this.onBackPressed, + @required this.onRefreshed, + @required this.isSaving, + @required this.isLoading, + @required this.isDirty, + }); + + factory CompanyGatewayViewVM.fromStore(Store store) { + final state = store.state; + final companyGateway = state.companyGatewayState.map[state.companyGatewayUIState.selectedId] ?? + CompanyGatewayEntity(id: state.companyGatewayUIState.selectedId); + + Future _handleRefresh(BuildContext context) { + final completer = snackBarCompleter( + context, AppLocalization.of(context).refreshComplete); + store.dispatch(LoadCompanyGateway(completer: completer, companyGatewayId: companyGateway.id)); + return completer.future; + } + + return CompanyGatewayViewVM( + state: state, + company: state.selectedCompany, + isSaving: state.isSaving, + isLoading: state.isLoading, + isDirty: companyGateway.isNew, + companyGateway: companyGateway, + onEditPressed: (BuildContext context) { + final Completer completer = Completer(); + store.dispatch(EditCompanyGateway( + companyGateway: companyGateway, context: context, completer: completer)); + completer.future.then((companyGateway) { + Scaffold.of(context).showSnackBar(SnackBar( + content: SnackBarRow( + message: AppLocalization.of(context).updatedCompanyGateway, + ))); + }); + }, + onRefreshed: (context) => _handleRefresh(context), + onBackPressed: () { + if (state.uiState.currentRoute.contains(CompanyGatewayScreen.route)) { + store.dispatch(UpdateCurrentRoute(CompanyGatewayScreen.route)); + } + }, + onEntityAction: (BuildContext context, EntityAction action) => + handleCompanyGatewayAction(context, companyGateway, action), + ); + } + + final AppState state; + final CompanyGatewayEntity companyGateway; + final CompanyEntity company; + final Function(BuildContext, EntityAction) onEntityAction; + final Function(BuildContext) onEditPressed; + final Function onBackPressed; + final Function(BuildContext) onRefreshed; + final bool isSaving; + final bool isLoading; + final bool isDirty; +}