From a1518eb10bbedc832eb18cde0f5c13324c7eec8a Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Tue, 7 Jun 2022 09:23:49 +0300 Subject: [PATCH] Add view/copy CP links to client and invoice previews --- lib/ui/app/entity_header.dart | 2 +- lib/ui/app/lists/app_list_tile.dart | 12 ++-- lib/ui/app/portal_links.dart | 67 +++++++++++++++++++ lib/ui/client/view/client_view_details.dart | 29 ++------ lib/ui/client/view/client_view_fullwidth.dart | 38 ++--------- lib/ui/client/view/client_view_overview.dart | 11 +++ .../invoice/view/invoice_view_contacts.dart | 32 ++------- .../invoice/view/invoice_view_overview.dart | 13 ++++ 8 files changed, 113 insertions(+), 91 deletions(-) create mode 100644 lib/ui/app/portal_links.dart diff --git a/lib/ui/app/entity_header.dart b/lib/ui/app/entity_header.dart index bc1cc0671..a41278a1b 100644 --- a/lib/ui/app/entity_header.dart +++ b/lib/ui/app/entity_header.dart @@ -112,7 +112,7 @@ class EntityHeader extends StatelessWidget { ), Padding( padding: statusLabel != null || !entity.isActive - ? const EdgeInsets.only(top: 25, bottom: 5) + ? const EdgeInsets.only(top: 25) : const EdgeInsets.all(0), child: Row( children: [ diff --git a/lib/ui/app/lists/app_list_tile.dart b/lib/ui/app/lists/app_list_tile.dart index ef51323ad..ac93403eb 100644 --- a/lib/ui/app/lists/app_list_tile.dart +++ b/lib/ui/app/lists/app_list_tile.dart @@ -17,7 +17,7 @@ class AppListTile extends StatelessWidget { this.dense = false, this.onTap, this.copyValue, - this.buttons, + this.buttonRow, }); final IconData icon; @@ -26,7 +26,7 @@ class AppListTile extends StatelessWidget { final bool dense; final Function onTap; final String copyValue; - final List buttons; + final Widget buttonRow; void _onLongPress(BuildContext context) { if ((copyValue ?? title ?? '').isEmpty) { @@ -47,18 +47,16 @@ class AppListTile extends StatelessWidget { contentPadding: EdgeInsets.symmetric(horizontal: 25, vertical: 16), leading: Icon(icon), title: Text(title), - subtitle: buttons != null || subtitle != null + subtitle: buttonRow != null || subtitle != null ? Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ if (subtitle != null) Text(subtitle), - if (buttons != null) + if (buttonRow != null) Padding( padding: const EdgeInsets.only(top: 8), - child: Row( - children: buttons, - ), + child: buttonRow, ) ], ) diff --git a/lib/ui/app/portal_links.dart b/lib/ui/app/portal_links.dart new file mode 100644 index 000000000..2ea0ea563 --- /dev/null +++ b/lib/ui/app/portal_links.dart @@ -0,0 +1,67 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_styled_toast/flutter_styled_toast.dart'; +import 'package:invoiceninja_flutter/constants.dart'; +import 'package:invoiceninja_flutter/data/models/client_model.dart'; +import 'package:invoiceninja_flutter/utils/localization.dart'; +import 'package:url_launcher/url_launcher.dart'; + +class PortalLinks extends StatelessWidget { + const PortalLinks({ + Key key, + @required this.viewLink, + @required this.copyLink, + @required this.client, + }) : super(key: key); + + final String viewLink; + final String copyLink; + final ClientEntity client; + + @override + Widget build(BuildContext context) { + final localization = AppLocalization.of(context); + + var viewLinkWithHash = viewLink; + if (!viewLink.contains('?')) { + viewLinkWithHash += '?'; + } + viewLinkWithHash += '&client_hash=${client.clientHash}'; + + var copyLinkWithHash = copyLink; + if (!copyLink.contains('?')) { + copyLinkWithHash += '?'; + } + copyLinkWithHash += '&client_hash=${client.clientHash}'; + + return Row( + children: [ + Expanded( + child: OutlinedButton( + onPressed: () => launch(viewLinkWithHash), + child: Text( + localization.viewPortal, + maxLines: 2, + overflow: TextOverflow.ellipsis, + textAlign: TextAlign.center, + )), + ), + SizedBox(width: 8), + Expanded( + child: OutlinedButton( + onPressed: () { + Clipboard.setData(ClipboardData(text: copyLinkWithHash)); + showToast( + localization.copiedToClipboard.replaceFirst(':value ', '')); + }, + child: Text( + localization.copyLink, + maxLines: 2, + overflow: TextOverflow.ellipsis, + textAlign: TextAlign.center, + )), + ), + ], + ); + } +} diff --git a/lib/ui/client/view/client_view_details.dart b/lib/ui/client/view/client_view_details.dart index 6e8648b95..40b49ce28 100644 --- a/lib/ui/client/view/client_view_details.dart +++ b/lib/ui/client/view/client_view_details.dart @@ -3,16 +3,14 @@ import 'dart:async'; // Flutter imports: import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'package:flutter_redux/flutter_redux.dart'; // Package imports: -import 'package:flutter_styled_toast/flutter_styled_toast.dart'; import 'package:invoiceninja_flutter/redux/app/app_state.dart'; +import 'package:invoiceninja_flutter/ui/app/portal_links.dart'; import 'package:url_launcher/url_launcher.dart'; // Project imports: -import 'package:invoiceninja_flutter/constants.dart'; import 'package:invoiceninja_flutter/data/models/models.dart'; import 'package:invoiceninja_flutter/ui/app/lists/app_list_tile.dart'; import 'package:invoiceninja_flutter/ui/app/scrollable_listview.dart'; @@ -61,26 +59,11 @@ class _ClientViewDetailsState extends State { contacts.forEach((contact) { listTiles.add(AppListTile( - buttons: [ - Expanded( - child: OutlinedButton( - child: Text(localization.viewPortal.toUpperCase()), - onPressed: () { - launch( - '${contact.silentLink}&client_hash=${client.clientHash}'); - }, - )), - SizedBox(width: kTableColumnGap), - Expanded( - child: OutlinedButton( - child: Text(localization.copyLink.toUpperCase()), - onPressed: () { - Clipboard.setData(ClipboardData(text: contact.link)); - showToast( - localization.copiedToClipboard.replaceFirst(':value ', '')); - }, - )), - ], + buttonRow: PortalLinks( + viewLink: contact.silentLink, + copyLink: contact.link, + client: client, + ), icon: Icons.email, title: contact.fullName.isEmpty ? localization.blankContact diff --git a/lib/ui/client/view/client_view_fullwidth.dart b/lib/ui/client/view/client_view_fullwidth.dart index d4b2cc8c2..5324f7fc6 100644 --- a/lib/ui/client/view/client_view_fullwidth.dart +++ b/lib/ui/client/view/client_view_fullwidth.dart @@ -1,7 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'package:flutter_redux/flutter_redux.dart'; -import 'package:flutter_styled_toast/flutter_styled_toast.dart'; import 'package:invoiceninja_flutter/constants.dart'; import 'package:invoiceninja_flutter/redux/app/app_state.dart'; import 'package:invoiceninja_flutter/ui/app/copy_to_clipboard.dart'; @@ -9,6 +7,7 @@ import 'package:invoiceninja_flutter/ui/app/entity_header.dart'; import 'package:invoiceninja_flutter/ui/app/form_card.dart'; import 'package:invoiceninja_flutter/ui/app/forms/app_tab_bar.dart'; import 'package:invoiceninja_flutter/ui/app/icon_text.dart'; +import 'package:invoiceninja_flutter/ui/app/portal_links.dart'; import 'package:invoiceninja_flutter/ui/client/view/client_view_activity.dart'; import 'package:invoiceninja_flutter/ui/client/view/client_view_documents.dart'; import 'package:invoiceninja_flutter/ui/client/view/client_view_ledger.dart'; @@ -268,37 +267,10 @@ class _ClientViewFullwidthState extends State ), ), SizedBox(height: 8), - Row( - children: [ - Flexible( - child: OutlinedButton( - onPressed: () => launch( - '${contact.silentLink}&client_hash=${client.clientHash}'), - child: Text( - localization.clientPortal, - textAlign: TextAlign.center, - maxLines: 2, - overflow: TextOverflow.ellipsis, - )), - ), - SizedBox(width: 4), - Flexible( - child: OutlinedButton( - onPressed: () { - final url = - '${contact.link}&client_hash=${client.clientHash}'; - Clipboard.setData(ClipboardData(text: url)); - showToast(localization.copiedToClipboard - .replaceFirst(':value ', '')); - }, - child: Text( - localization.copyLink, - textAlign: TextAlign.center, - maxLines: 2, - overflow: TextOverflow.ellipsis, - )), - ), - ], + PortalLinks( + viewLink: contact.silentLink, + copyLink: contact.link, + client: client, ), SizedBox(height: 16), ], diff --git a/lib/ui/client/view/client_view_overview.dart b/lib/ui/client/view/client_view_overview.dart index 696cea639..424f0970c 100644 --- a/lib/ui/client/view/client_view_overview.dart +++ b/lib/ui/client/view/client_view_overview.dart @@ -1,5 +1,6 @@ // Flutter imports: import 'package:flutter/material.dart'; +import 'package:invoiceninja_flutter/ui/app/portal_links.dart'; // Package imports: import 'package:url_launcher/url_launcher.dart'; @@ -47,6 +48,7 @@ class ClientOverview extends StatelessWidget { final statics = state.staticState; final fields = {}; final group = client.hasGroup ? state.groupState.map[client.groupId] : null; + final contact = client.primaryContact; final user = client.hasUser ? state.userState.get(client.assignedUserId) : null; @@ -127,6 +129,15 @@ class ClientOverview extends StatelessWidget { formatNumber(client.balance, context, clientId: client.id), ), ListDivider(), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 16), + child: PortalLinks( + viewLink: contact.silentLink, + copyLink: contact.link, + client: client, + ), + ), + ListDivider(), if ((client.privateNotes ?? '').isNotEmpty) ...[ IconMessage(client.privateNotes, iconData: Icons.lock), ListDivider() diff --git a/lib/ui/invoice/view/invoice_view_contacts.dart b/lib/ui/invoice/view/invoice_view_contacts.dart index f7200c6b0..88ab6eeeb 100644 --- a/lib/ui/invoice/view/invoice_view_contacts.dart +++ b/lib/ui/invoice/view/invoice_view_contacts.dart @@ -1,13 +1,10 @@ // Flutter imports: import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; // Package imports: -import 'package:flutter_styled_toast/flutter_styled_toast.dart'; -import 'package:url_launcher/url_launcher.dart'; +import 'package:invoiceninja_flutter/ui/app/portal_links.dart'; // Project imports: -import 'package:invoiceninja_flutter/constants.dart'; import 'package:invoiceninja_flutter/data/models/models.dart'; import 'package:invoiceninja_flutter/ui/app/scrollable_listview.dart'; import 'package:invoiceninja_flutter/ui/invoice/view/invoice_view_vm.dart'; @@ -115,29 +112,10 @@ class _InvitationListTile extends StatelessWidget { ), ), SizedBox(height: 8), - Row( - children: [ - Expanded( - child: OutlinedButton( - child: Text(localization.viewPortal.toUpperCase()), - onPressed: () { - launch( - '${invitation.silentLink}&client_hash=${client.clientHash}', - forceWebView: false, - forceSafariVC: false); - }, - )), - SizedBox(width: kTableColumnGap), - Expanded( - child: OutlinedButton( - child: Text(localization.copyLink.toUpperCase()), - onPressed: () { - Clipboard.setData(ClipboardData(text: invitation.link)); - showToast(localization.copiedToClipboard - .replaceFirst(':value ', '')); - }, - )), - ], + PortalLinks( + viewLink: invitation.silentLink, + copyLink: invitation.link, + client: client, ) ], ), diff --git a/lib/ui/invoice/view/invoice_view_overview.dart b/lib/ui/invoice/view/invoice_view_overview.dart index 2991023b5..b38694ad9 100644 --- a/lib/ui/invoice/view/invoice_view_overview.dart +++ b/lib/ui/invoice/view/invoice_view_overview.dart @@ -21,6 +21,7 @@ import 'package:invoiceninja_flutter/ui/app/entity_header.dart'; import 'package:invoiceninja_flutter/ui/app/icon_message.dart'; import 'package:invoiceninja_flutter/ui/app/invoice/invoice_item_view.dart'; import 'package:invoiceninja_flutter/ui/app/lists/list_divider.dart'; +import 'package:invoiceninja_flutter/ui/app/portal_links.dart'; import 'package:invoiceninja_flutter/ui/app/scrollable_listview.dart'; import 'package:invoiceninja_flutter/ui/invoice/view/invoice_view_vm.dart'; import 'package:invoiceninja_flutter/utils/formatting.dart'; @@ -113,6 +114,18 @@ class InvoiceOverview extends StatelessWidget { ListDivider(), ]; + widgets.addAll([ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 16), + child: PortalLinks( + viewLink: invoice.invitationSilentLink, + copyLink: invoice.invitationLink, + client: client, + ), + ), + ListDivider(), + ]); + if ((invoice.privateNotes ?? '').isNotEmpty) { widgets.addAll([ IconMessage(