diff --git a/lib/data/models/company_model.dart b/lib/data/models/company_model.dart index b1dc9604a..530ab43f9 100644 --- a/lib/data/models/company_model.dart +++ b/lib/data/models/company_model.dart @@ -258,7 +258,6 @@ abstract class CompanyEntity extends Object } } - // TODO remove this if ([ EntityType.recurringInvoice, @@ -437,10 +436,18 @@ abstract class UserCompanyEntity String get permissions; - bool can(UserPermission permission, EntityType entityType) => - (isAdmin ?? false) || - permissions.contains('${permission}_all') || - permissions.contains('${permission}_$entityType'); + bool can(UserPermission permission, EntityType entityType) { + if (entityType == null) { + return false; + } + + if (isAdmin ?? false) { + return true; + } + + return permissions.contains('${permission}_all') || + permissions.contains('${permission}_$entityType'); + } bool canView(EntityType entityType) => can(UserPermission.view, entityType); @@ -1478,7 +1485,6 @@ abstract class SettingsEntity @BuiltValueField(wireName: 'email_template_reminder4') String get emailBodyReminder4; - // Added @nullable @BuiltValueField(wireName: 'enable_client_portal') @@ -1496,7 +1502,6 @@ abstract class SettingsEntity @BuiltValueField(wireName: 'client_manual_payment_notification') bool get clientManualPaymentNotification; - // TODO remove this field @nullable @BuiltValueField(wireName: 'custom_payment_terms') diff --git a/lib/redux/app/app_actions.dart b/lib/redux/app/app_actions.dart index 587a854de..beee7bbe7 100644 --- a/lib/redux/app/app_actions.dart +++ b/lib/redux/app/app_actions.dart @@ -1,7 +1,10 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_redux/flutter_redux.dart'; +import 'package:invoiceninja_flutter/data/models/company_gateway_model.dart'; import 'package:invoiceninja_flutter/data/models/entities.dart'; +import 'package:invoiceninja_flutter/data/models/group_model.dart'; +import 'package:invoiceninja_flutter/data/models/models.dart'; import 'package:invoiceninja_flutter/data/models/static/static_data_model.dart'; import 'package:invoiceninja_flutter/redux/app/app_state.dart'; import 'package:invoiceninja_flutter/redux/client/client_actions.dart'; @@ -108,6 +111,61 @@ class FilterCompany implements PersistUI { final String filter; } +void viewEntitiesByType({BuildContext context, EntityType entityType}) { + final store = StoreProvider.of(context); + switch (entityType) { + case EntityType.client: + store.dispatch(ViewClientList(context: context)); + break; + case EntityType.user: + store.dispatch(ViewUserList(context: context)); + break; + case EntityType.project: + store.dispatch(ViewProjectList(context: context)); + break; + case EntityType.taxRate: + store.dispatch(ViewTaxRateList(context: context)); + break; + case EntityType.companyGateway: + store.dispatch(ViewCompanyGatewayList(context: context)); + break; + case EntityType.invoice: + store.dispatch(ViewInvoiceList(context: context)); + break; + //case EntityType.recurringInvoice: + //store.dispatch(ViewRecurringInvoice(recurringInvoiceId: entityId, context: context)); + //break; + case EntityType.quote: + store.dispatch(ViewQuoteList(context: context)); + break; + case EntityType.vendor: + store.dispatch(ViewVendorList(context: context)); + break; + case EntityType.product: + store.dispatch(ViewProductList(context: context)); + break; + case EntityType.task: + store.dispatch(ViewTaskList(context: context)); + break; + case EntityType.expense: + store.dispatch(ViewExpenseList(context: context)); + break; + //case EntityType.expenseCategory: + //store.dispatch(ViewExpenseCategory(taxRateId: entityId, context: context)); + //break; + //case EntityType.credit: + //store.dispatch(ViewCredit(creditId: entityId, context: context)); + //break; + case EntityType.payment: + store.dispatch(ViewPaymentList(context: context)); + break; + case EntityType.group: + store.dispatch(ViewGroupList(context: context)); + break; + // TODO Add to starter + } +} + void viewEntityById( {BuildContext context, String entityId, EntityType entityType}) { final store = StoreProvider.of(context); @@ -165,6 +223,72 @@ void viewEntityById( } } +void createEntityByType({BuildContext context, EntityType entityType}) { + final store = StoreProvider.of(context); + final state = store.state; + final company = state.company; + switch (entityType) { + case EntityType.client: + store.dispatch(EditClient(context: context, client: ClientEntity())); + break; + case EntityType.user: + store.dispatch(EditUser(context: context, user: UserEntity())); + break; + case EntityType.project: + store.dispatch(EditProject(context: context, project: ProjectEntity())); + break; + case EntityType.taxRate: + store.dispatch(EditTaxRate(context: context, taxRate: TaxRateEntity())); + break; + case EntityType.companyGateway: + store.dispatch(EditCompanyGateway( + context: context, companyGateway: CompanyGatewayEntity())); + break; + case EntityType.invoice: + store.dispatch(EditInvoice( + context: context, invoice: InvoiceEntity(company: company))); + break; + //case EntityType.recurringInvoice: + //store.dispatch(ViewRecurringInvoice(recurringInvoiceId: entityId, context: context)); + //break; + case EntityType.quote: + store.dispatch(EditQuote( + context: context, + quote: InvoiceEntity(company: company, isQuote: true))); + break; + case EntityType.vendor: + store.dispatch(EditVendor(context: context, vendor: VendorEntity())); + break; + case EntityType.product: + store.dispatch(EditProduct(context: context, product: ProductEntity())); + break; + case EntityType.task: + store.dispatch(EditTask( + context: context, + task: TaskEntity(isRunning: state.prefState.autoStartTasks))); + break; + case EntityType.expense: + store.dispatch(EditExpense( + context: context, + expense: ExpenseEntity(prefState: state.prefState))); + break; + //case EntityType.expenseCategory: + //store.dispatch(ViewExpenseCategory(taxRateId: entityId, context: context)); + //break; + //case EntityType.credit: + //store.dispatch(ViewCredit(creditId: entityId, context: context)); + //break; + case EntityType.payment: + store.dispatch(EditPayment( + context: context, payment: PaymentEntity(company: company))); + break; + case EntityType.group: + store.dispatch(EditGroup(context: context, group: GroupEntity())); + break; + // TODO Add to starter + } +} + void editEntityById( {BuildContext context, String entityId, EntityType entityType}) { final store = StoreProvider.of(context); diff --git a/lib/ui/app/menu_drawer.dart b/lib/ui/app/menu_drawer.dart index e32eacb0d..77e4dd023 100644 --- a/lib/ui/app/menu_drawer.dart +++ b/lib/ui/app/menu_drawer.dart @@ -1,10 +1,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; -import 'package:invoiceninja_flutter/redux/expense/expense_actions.dart'; -import 'package:invoiceninja_flutter/redux/project/project_actions.dart'; -import 'package:invoiceninja_flutter/redux/task/task_actions.dart'; -import 'package:invoiceninja_flutter/redux/vendor/vendor_actions.dart'; +import 'package:invoiceninja_flutter/redux/app/app_actions.dart'; import 'package:invoiceninja_flutter/ui/app/debug/state_inspector.dart'; import 'package:invoiceninja_flutter/ui/app/loading_indicator.dart'; import 'package:invoiceninja_flutter/ui/app/resources/cached_image.dart'; @@ -14,10 +11,7 @@ import 'package:flutter_redux/flutter_redux.dart'; import 'package:invoiceninja_flutter/constants.dart'; import 'package:invoiceninja_flutter/data/models/models.dart'; import 'package:invoiceninja_flutter/redux/app/app_state.dart'; -import 'package:invoiceninja_flutter/redux/client/client_actions.dart'; import 'package:invoiceninja_flutter/redux/dashboard/dashboard_actions.dart'; -import 'package:invoiceninja_flutter/redux/invoice/invoice_actions.dart'; -import 'package:invoiceninja_flutter/redux/product/product_actions.dart'; import 'package:invoiceninja_flutter/redux/settings/settings_actions.dart'; import 'package:invoiceninja_flutter/ui/app/menu_drawer_vm.dart'; import 'package:invoiceninja_flutter/ui/app/lists/selected_indicator.dart'; @@ -25,8 +19,6 @@ import 'package:invoiceninja_flutter/utils/icons.dart'; import 'package:invoiceninja_flutter/utils/localization.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:invoiceninja_flutter/utils/platforms.dart'; -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 @@ -139,7 +131,6 @@ class MenuDrawer extends StatelessWidget { )); final Store store = StoreProvider.of(context); - final NavigatorState navigator = Navigator.of(context); final state = store.state; final enableDarkMode = state.prefState.enableDarkMode; final localization = AppLocalization.of(context); @@ -183,79 +174,30 @@ class MenuDrawer extends StatelessWidget { entityType: EntityType.client, icon: getEntityIcon(EntityType.client), title: localization.clients, - onTap: () => - store.dispatch(ViewClientList(context: context)), - onCreateTap: () { - if (isMobile(context)) { - navigator.pop(); - } - store.dispatch(EditClient( - client: ClientEntity(), context: context)); - }, ), DrawerTile( company: company, entityType: EntityType.product, icon: getEntityIcon(EntityType.product), title: localization.products, - onTap: () { - store.dispatch(ViewProductList(context: context)); - }, - onCreateTap: () { - if (isMobile(context)) { - navigator.pop(); - } - store.dispatch(EditProduct( - product: ProductEntity(), context: context)); - }, ), DrawerTile( company: company, entityType: EntityType.invoice, icon: getEntityIcon(EntityType.invoice), title: localization.invoices, - onTap: () => - store.dispatch(ViewInvoiceList(context: context)), - onCreateTap: () { - if (isMobile(context)) { - navigator.pop(); - } - store.dispatch(EditInvoice( - invoice: InvoiceEntity(company: company), - context: context)); - }, ), DrawerTile( company: company, entityType: EntityType.payment, icon: getEntityIcon(EntityType.payment), title: localization.payments, - onTap: () => - store.dispatch(ViewPaymentList(context: context)), - onCreateTap: () { - if (isMobile(context)) { - navigator.pop(); - } - store.dispatch(EditPayment( - payment: PaymentEntity(company: company), - context: context)); - }, ), DrawerTile( company: company, entityType: EntityType.quote, icon: getEntityIcon(EntityType.quote), title: localization.quotes, - onTap: () => - store.dispatch(ViewQuoteList(context: context)), - onCreateTap: () { - if (isMobile(context)) { - navigator.pop(); - } - store.dispatch(EditQuote( - quote: InvoiceEntity(isQuote: true), - context: context)); - }, ), if (Config.DEMO_MODE) ...[ DrawerTile( @@ -263,64 +205,24 @@ class MenuDrawer extends StatelessWidget { entityType: EntityType.project, icon: getEntityIcon(EntityType.project), title: localization.projects, - onTap: () => - store.dispatch(ViewProjectList(context: context)), - onCreateTap: () { - if (isMobile(context)) { - navigator.pop(); - } - store.dispatch(EditProject( - project: ProjectEntity(), context: context)); - }, ), DrawerTile( company: company, entityType: EntityType.task, icon: getEntityIcon(EntityType.task), title: localization.tasks, - onTap: () => - store.dispatch(ViewTaskList(context: context)), - onCreateTap: () { - if (isMobile(context)) { - navigator.pop(); - } - store.dispatch(EditTask( - task: TaskEntity( - isRunning: state.prefState.autoStartTasks), - context: context)); - }, ), DrawerTile( company: company, entityType: EntityType.vendor, icon: getEntityIcon(EntityType.vendor), title: localization.vendors, - onTap: () => - store.dispatch(ViewVendorList(context: context)), - onCreateTap: () { - if (isMobile(context)) { - navigator.pop(); - } - store.dispatch(EditVendor( - vendor: VendorEntity(), context: context)); - }, ), DrawerTile( company: company, entityType: EntityType.expense, icon: getEntityIcon(EntityType.expense), title: localization.expenses, - onTap: () => - store.dispatch(ViewExpenseList(context: context)), - onCreateTap: () { - if (isMobile(context)) { - navigator.pop(); - } - store.dispatch(EditExpense( - expense: ExpenseEntity( - company: company, prefState: state.prefState), - context: context)); - }, ), ], // STARTER: menu - do not remove comment @@ -329,9 +231,6 @@ class MenuDrawer extends StatelessWidget { icon: kIsWeb ? Icons.settings : FontAwesomeIcons.cog, title: localization.settings, onTap: () { - if (isMobile(context)) { - navigator.pop(); - } store.dispatch(ViewSettings( context: context, userCompany: state.userCompany)); @@ -355,9 +254,10 @@ class DrawerTile extends StatelessWidget { @required this.company, @required this.icon, @required this.title, - @required this.onTap, - this.onCreateTap, + this.onTap, this.entityType, + this.onLongPress, + this.onCreateTap, }); final CompanyEntity company; @@ -365,6 +265,7 @@ class DrawerTile extends StatelessWidget { final IconData icon; final String title; final Function onTap; + final Function onLongPress; final Function onCreateTap; @override @@ -372,6 +273,7 @@ class DrawerTile extends StatelessWidget { final store = StoreProvider.of(context); final uiState = store.state.uiState; final userCompany = store.state.userCompany; + final NavigatorState navigator = Navigator.of(context); if (entityType != null && !userCompany.canViewOrCreate(entityType)) { return Container(); @@ -389,13 +291,23 @@ class DrawerTile extends StatelessWidget { child: ListTile( dense: true, leading: Icon(icon, size: 22), - title: Tooltip(message: title, child: Text(title)), - onTap: onTap, - trailing: onCreateTap == null || !userCompany.canCreate(entityType) + title: Text(title), + onTap: () => entityType != null + ? viewEntitiesByType(context: context, entityType: entityType) + : onTap(), + onLongPress: () => entityType != null + ? createEntityByType(context: context, entityType: entityType) + : null, + trailing: !userCompany.canCreate(entityType) ? null : IconButton( icon: Icon(Icons.add_circle_outline), - onPressed: onCreateTap, + onPressed: () { + if (isMobile(context)) { + navigator.pop(); + } + createEntityByType(context: context, entityType: entityType); + }, ), ), );