diff --git a/lib/ui/app/copy_to_clipboard.dart b/lib/ui/app/copy_to_clipboard.dart index dede59550..3d5b0c839 100644 --- a/lib/ui/app/copy_to_clipboard.dart +++ b/lib/ui/app/copy_to_clipboard.dart @@ -9,11 +9,13 @@ class CopyToClipboard extends StatelessWidget { @required this.value, this.child, this.showBorder = false, + this.onLongPress, }) : super(key: key); final Widget child; final String value; final bool showBorder; + final Function onLongPress; @override Widget build(BuildContext context) { @@ -41,13 +43,18 @@ class CopyToClipboard extends StatelessWidget { if (showBorder) { return ConstrainedBox( - child: OutlinedButton(onPressed: onTap, child: widget), + child: OutlinedButton( + onPressed: onTap, + child: widget, + onLongPress: onLongPress, + ), constraints: BoxConstraints(maxWidth: 180), ); } else { return InkWell( child: widget, onTap: onTap, + onLongPress: onLongPress, ); } } diff --git a/lib/ui/app/link_text.dart b/lib/ui/app/link_text.dart index 4dfb6ae71..10fda7c87 100644 --- a/lib/ui/app/link_text.dart +++ b/lib/ui/app/link_text.dart @@ -29,6 +29,9 @@ class LinkTextRelatedEntity extends StatelessWidget { viewEntity(entity: relation); viewEntity(entity: entity, addToStack: true); }, + onLongPress: () { + editEntity(context: context, entity: entity); + }, ); } } diff --git a/lib/ui/app/presenters/entity_presenter.dart b/lib/ui/app/presenters/entity_presenter.dart index 8421efa84..fa9186da5 100644 --- a/lib/ui/app/presenters/entity_presenter.dart +++ b/lib/ui/app/presenters/entity_presenter.dart @@ -7,6 +7,7 @@ import 'package:flutter_redux/flutter_redux.dart'; // Project imports: import 'package:invoiceninja_flutter/data/models/entities.dart'; import 'package:invoiceninja_flutter/redux/app/app_state.dart'; +import 'package:invoiceninja_flutter/ui/app/link_text.dart'; import 'package:invoiceninja_flutter/utils/formatting.dart'; import 'package:invoiceninja_flutter/utils/localization.dart'; @@ -88,11 +89,11 @@ class EntityPresenter { ? localization.archived : localization.deleted); case EntityFields.createdBy: - return Text( - state.userState.map[entity.createdUserId]?.listDisplayName ?? ''); + final user = state.userState.get(entity.createdUserId); + return LinkTextRelatedEntity(entity: user, relation: entity); case EntityFields.assignedTo: - return Text( - state.userState.map[entity.assignedUserId]?.listDisplayName ?? ''); + final user = state.userState.get(entity.assignedUserId); + return LinkTextRelatedEntity(entity: user, relation: entity); case EntityFields.isDeleted: return Text(entity.isDeleted ? localization.yes : localization.no); } diff --git a/lib/ui/client/client_presenter.dart b/lib/ui/client/client_presenter.dart index 8d430f21d..0711a97d0 100644 --- a/lib/ui/client/client_presenter.dart +++ b/lib/ui/client/client_presenter.dart @@ -10,6 +10,7 @@ import 'package:invoiceninja_flutter/redux/app/app_state.dart'; import 'package:invoiceninja_flutter/ui/app/copy_to_clipboard.dart'; import 'package:invoiceninja_flutter/ui/app/presenters/entity_presenter.dart'; import 'package:invoiceninja_flutter/utils/formatting.dart'; +import 'package:url_launcher/url_launcher.dart'; class ClientPresenter extends EntityPresenter { static List getDefaultTableFields(UserCompanyEntity userCompany) { @@ -68,6 +69,7 @@ class ClientPresenter extends EntityPresenter { return CopyToClipboard( value: client.primaryContact.email, showBorder: true, + onLongPress: () => launch('mailto:${client.primaryContact.email}'), ); case ClientFields.contactPhone: return Text(client.primaryContact.phone); diff --git a/lib/ui/credit/credit_presenter.dart b/lib/ui/credit/credit_presenter.dart index eda1e26b9..cc33b363a 100644 --- a/lib/ui/credit/credit_presenter.dart +++ b/lib/ui/credit/credit_presenter.dart @@ -14,6 +14,7 @@ import 'package:invoiceninja_flutter/ui/app/link_text.dart'; import 'package:invoiceninja_flutter/ui/app/presenters/entity_presenter.dart'; import 'package:invoiceninja_flutter/utils/formatting.dart'; import 'package:invoiceninja_flutter/utils/localization.dart'; +import 'package:url_launcher/url_launcher.dart'; class CreditPresenter extends EntityPresenter { static List getDefaultTableFields(UserCompanyEntity userCompany) { @@ -127,7 +128,8 @@ class CreditPresenter extends EntityPresenter { final project = state.projectState.get(credit.projectId); return LinkTextRelatedEntity(entity: project, relation: credit); case CreditFields.vendor: - return Text(state.vendorState.get(credit.vendorId).name); + final vendor = state.vendorState.get(credit.vendorId); + return LinkTextRelatedEntity(entity: vendor, relation: credit); case CreditFields.clientState: return Text(client.state); case CreditFields.clientCity: @@ -140,12 +142,16 @@ class CreditPresenter extends EntityPresenter { case CreditFields.contactEmail: final contact = creditContactSelector( credit, state.clientState.get(credit.clientId)); + if (contact == null) { + return SizedBox(); + } if (field == CreditFields.contactName) { - return Text(contact?.fullName ?? ''); + return Text(contact.fullName ?? ''); } return CopyToClipboard( - value: contact?.email ?? '', + value: contact.email, showBorder: true, + onLongPress: () => launch('mailto:${contact.email}'), ); case CreditFields.partial: return Text(formatNumber(credit.partial, context)); diff --git a/lib/ui/expense/expense_presenter.dart b/lib/ui/expense/expense_presenter.dart index bf4af2c2e..4c141cbd7 100644 --- a/lib/ui/expense/expense_presenter.dart +++ b/lib/ui/expense/expense_presenter.dart @@ -72,8 +72,8 @@ class ExpensePresenter extends EntityPresenter { return EntityStatusChip(entity: expense, showState: true); case ExpenseFields.vendor: case ExpenseFields.vendorId: - return Text((state.vendorState.map[expense.vendorId] ?? VendorEntity()) - .listDisplayName); + final vendor = state.vendorState.get(expense.vendorId); + return LinkTextRelatedEntity(entity: vendor, relation: expense); case ExpenseFields.clientId: case ExpenseFields.client: final client = state.clientState.get(expense.clientId); diff --git a/lib/ui/invoice/invoice_presenter.dart b/lib/ui/invoice/invoice_presenter.dart index aca194211..b0e359e66 100644 --- a/lib/ui/invoice/invoice_presenter.dart +++ b/lib/ui/invoice/invoice_presenter.dart @@ -14,6 +14,7 @@ import 'package:invoiceninja_flutter/ui/app/link_text.dart'; import 'package:invoiceninja_flutter/ui/app/presenters/entity_presenter.dart'; import 'package:invoiceninja_flutter/utils/formatting.dart'; import 'package:invoiceninja_flutter/utils/localization.dart'; +import 'package:url_launcher/url_launcher.dart'; class InvoicePresenter extends EntityPresenter { static List getDefaultTableFields(UserCompanyEntity userCompany) { @@ -84,7 +85,8 @@ class InvoicePresenter extends EntityPresenter { final project = state.projectState.get(invoice.projectId); return LinkTextRelatedEntity(entity: project, relation: invoice); case InvoiceFields.vendor: - return Text(state.vendorState.get(invoice.vendorId).name); + final vendor = state.vendorState.get(invoice.vendorId); + return LinkTextRelatedEntity(entity: vendor, relation: invoice); case InvoiceFields.date: return Text(formatDate(invoice.date, context)); case InvoiceFields.lastSentDate: @@ -159,12 +161,16 @@ class InvoicePresenter extends EntityPresenter { case InvoiceFields.contactEmail: final contact = invoiceContactSelector( invoice, state.clientState.get(invoice.clientId)); + if (contact == null) { + return SizedBox(); + } if (field == InvoiceFields.contactName) { - return Text(contact?.fullName ?? ''); + return Text(contact.fullName); } return CopyToClipboard( - value: contact?.email ?? '', + value: contact.email, showBorder: true, + onLongPress: () => launch('mailto:${contact.email}'), ); case InvoiceFields.partial: return Text(formatNumber(invoice.partial, context)); diff --git a/lib/ui/quote/quote_presenter.dart b/lib/ui/quote/quote_presenter.dart index f4d270dc1..9284981dc 100644 --- a/lib/ui/quote/quote_presenter.dart +++ b/lib/ui/quote/quote_presenter.dart @@ -15,6 +15,7 @@ import 'package:invoiceninja_flutter/ui/app/link_text.dart'; import 'package:invoiceninja_flutter/ui/app/presenters/entity_presenter.dart'; import 'package:invoiceninja_flutter/utils/formatting.dart'; import 'package:invoiceninja_flutter/utils/localization.dart'; +import 'package:url_launcher/url_launcher.dart'; class QuotePresenter extends EntityPresenter { static List getDefaultTableFields(UserCompanyEntity userCompany) { @@ -120,7 +121,8 @@ class QuotePresenter extends EntityPresenter { final project = state.projectState.get(quote.projectId); return LinkTextRelatedEntity(entity: project, relation: quote); case QuoteFields.vendor: - return Text(state.vendorState.get(quote.vendorId).name); + final vendor = state.vendorState.get(quote.vendorId); + return LinkTextRelatedEntity(entity: vendor, relation: quote); case QuoteFields.clientState: return Text(client.state); case QuoteFields.clientCity: @@ -133,12 +135,16 @@ class QuotePresenter extends EntityPresenter { case QuoteFields.contactEmail: final contact = quoteContactSelector(quote, state.clientState.get(quote.clientId)); + if (contact == null) { + return SizedBox(); + } if (field == QuoteFields.contactName) { - return Text(contact?.fullName ?? ''); + return Text(contact.fullName); } return CopyToClipboard( - value: contact?.email ?? '', + value: contact.email, showBorder: true, + onLongPress: () => launch('mailto:${contact.email}'), ); case QuoteFields.partial: return Text(formatNumber(quote.partial, context)); diff --git a/lib/ui/recurring_expense/recurring_expense_presenter.dart b/lib/ui/recurring_expense/recurring_expense_presenter.dart index 85f003a36..6dbe43f93 100644 --- a/lib/ui/recurring_expense/recurring_expense_presenter.dart +++ b/lib/ui/recurring_expense/recurring_expense_presenter.dart @@ -72,8 +72,8 @@ class RecurringExpensePresenter extends EntityPresenter { return EntityStatusChip(entity: expense, showState: true); case RecurringExpenseFields.vendor: case RecurringExpenseFields.vendorId: - return Text((state.vendorState.map[expense.vendorId] ?? VendorEntity()) - .listDisplayName); + final vendor = state.vendorState.get(expense.vendorId); + return LinkTextRelatedEntity(entity: vendor, relation: expense); case RecurringExpenseFields.clientId: case RecurringExpenseFields.client: final client = state.clientState.get(expense.clientId);