Add view/copy CP links to client and invoice previews
This commit is contained in:
parent
b02f913146
commit
a1518eb10b
|
|
@ -112,7 +112,7 @@ class EntityHeader extends StatelessWidget {
|
||||||
),
|
),
|
||||||
Padding(
|
Padding(
|
||||||
padding: statusLabel != null || !entity.isActive
|
padding: statusLabel != null || !entity.isActive
|
||||||
? const EdgeInsets.only(top: 25, bottom: 5)
|
? const EdgeInsets.only(top: 25)
|
||||||
: const EdgeInsets.all(0),
|
: const EdgeInsets.all(0),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ class AppListTile extends StatelessWidget {
|
||||||
this.dense = false,
|
this.dense = false,
|
||||||
this.onTap,
|
this.onTap,
|
||||||
this.copyValue,
|
this.copyValue,
|
||||||
this.buttons,
|
this.buttonRow,
|
||||||
});
|
});
|
||||||
|
|
||||||
final IconData icon;
|
final IconData icon;
|
||||||
|
|
@ -26,7 +26,7 @@ class AppListTile extends StatelessWidget {
|
||||||
final bool dense;
|
final bool dense;
|
||||||
final Function onTap;
|
final Function onTap;
|
||||||
final String copyValue;
|
final String copyValue;
|
||||||
final List<Widget> buttons;
|
final Widget buttonRow;
|
||||||
|
|
||||||
void _onLongPress(BuildContext context) {
|
void _onLongPress(BuildContext context) {
|
||||||
if ((copyValue ?? title ?? '').isEmpty) {
|
if ((copyValue ?? title ?? '').isEmpty) {
|
||||||
|
|
@ -47,18 +47,16 @@ class AppListTile extends StatelessWidget {
|
||||||
contentPadding: EdgeInsets.symmetric(horizontal: 25, vertical: 16),
|
contentPadding: EdgeInsets.symmetric(horizontal: 25, vertical: 16),
|
||||||
leading: Icon(icon),
|
leading: Icon(icon),
|
||||||
title: Text(title),
|
title: Text(title),
|
||||||
subtitle: buttons != null || subtitle != null
|
subtitle: buttonRow != null || subtitle != null
|
||||||
? Column(
|
? Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
if (subtitle != null) Text(subtitle),
|
if (subtitle != null) Text(subtitle),
|
||||||
if (buttons != null)
|
if (buttonRow != null)
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(top: 8),
|
padding: const EdgeInsets.only(top: 8),
|
||||||
child: Row(
|
child: buttonRow,
|
||||||
children: buttons,
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,16 +3,14 @@ import 'dart:async';
|
||||||
|
|
||||||
// Flutter imports:
|
// Flutter imports:
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
|
||||||
import 'package:flutter_redux/flutter_redux.dart';
|
import 'package:flutter_redux/flutter_redux.dart';
|
||||||
|
|
||||||
// Package imports:
|
// Package imports:
|
||||||
import 'package:flutter_styled_toast/flutter_styled_toast.dart';
|
|
||||||
import 'package:invoiceninja_flutter/redux/app/app_state.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';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
// Project imports:
|
// Project imports:
|
||||||
import 'package:invoiceninja_flutter/constants.dart';
|
|
||||||
import 'package:invoiceninja_flutter/data/models/models.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/lists/app_list_tile.dart';
|
||||||
import 'package:invoiceninja_flutter/ui/app/scrollable_listview.dart';
|
import 'package:invoiceninja_flutter/ui/app/scrollable_listview.dart';
|
||||||
|
|
@ -61,26 +59,11 @@ class _ClientViewDetailsState extends State<ClientViewDetails> {
|
||||||
|
|
||||||
contacts.forEach((contact) {
|
contacts.forEach((contact) {
|
||||||
listTiles.add(AppListTile(
|
listTiles.add(AppListTile(
|
||||||
buttons: [
|
buttonRow: PortalLinks(
|
||||||
Expanded(
|
viewLink: contact.silentLink,
|
||||||
child: OutlinedButton(
|
copyLink: contact.link,
|
||||||
child: Text(localization.viewPortal.toUpperCase()),
|
client: client,
|
||||||
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 ', ''));
|
|
||||||
},
|
|
||||||
)),
|
|
||||||
],
|
|
||||||
icon: Icons.email,
|
icon: Icons.email,
|
||||||
title: contact.fullName.isEmpty
|
title: contact.fullName.isEmpty
|
||||||
? localization.blankContact
|
? localization.blankContact
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,5 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
|
||||||
import 'package:flutter_redux/flutter_redux.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/constants.dart';
|
||||||
import 'package:invoiceninja_flutter/redux/app/app_state.dart';
|
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/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/form_card.dart';
|
||||||
import 'package:invoiceninja_flutter/ui/app/forms/app_tab_bar.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/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_activity.dart';
|
||||||
import 'package:invoiceninja_flutter/ui/client/view/client_view_documents.dart';
|
import 'package:invoiceninja_flutter/ui/client/view/client_view_documents.dart';
|
||||||
import 'package:invoiceninja_flutter/ui/client/view/client_view_ledger.dart';
|
import 'package:invoiceninja_flutter/ui/client/view/client_view_ledger.dart';
|
||||||
|
|
@ -268,37 +267,10 @@ class _ClientViewFullwidthState extends State<ClientViewFullwidth>
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
SizedBox(height: 8),
|
SizedBox(height: 8),
|
||||||
Row(
|
PortalLinks(
|
||||||
children: [
|
viewLink: contact.silentLink,
|
||||||
Flexible(
|
copyLink: contact.link,
|
||||||
child: OutlinedButton(
|
client: client,
|
||||||
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,
|
|
||||||
)),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
SizedBox(height: 16),
|
SizedBox(height: 16),
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
// Flutter imports:
|
// Flutter imports:
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:invoiceninja_flutter/ui/app/portal_links.dart';
|
||||||
|
|
||||||
// Package imports:
|
// Package imports:
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
@ -47,6 +48,7 @@ class ClientOverview extends StatelessWidget {
|
||||||
final statics = state.staticState;
|
final statics = state.staticState;
|
||||||
final fields = <String, String>{};
|
final fields = <String, String>{};
|
||||||
final group = client.hasGroup ? state.groupState.map[client.groupId] : null;
|
final group = client.hasGroup ? state.groupState.map[client.groupId] : null;
|
||||||
|
final contact = client.primaryContact;
|
||||||
final user =
|
final user =
|
||||||
client.hasUser ? state.userState.get(client.assignedUserId) : null;
|
client.hasUser ? state.userState.get(client.assignedUserId) : null;
|
||||||
|
|
||||||
|
|
@ -127,6 +129,15 @@ class ClientOverview extends StatelessWidget {
|
||||||
formatNumber(client.balance, context, clientId: client.id),
|
formatNumber(client.balance, context, clientId: client.id),
|
||||||
),
|
),
|
||||||
ListDivider(),
|
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) ...[
|
if ((client.privateNotes ?? '').isNotEmpty) ...[
|
||||||
IconMessage(client.privateNotes, iconData: Icons.lock),
|
IconMessage(client.privateNotes, iconData: Icons.lock),
|
||||||
ListDivider()
|
ListDivider()
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,10 @@
|
||||||
// Flutter imports:
|
// Flutter imports:
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
|
||||||
|
|
||||||
// Package imports:
|
// Package imports:
|
||||||
import 'package:flutter_styled_toast/flutter_styled_toast.dart';
|
import 'package:invoiceninja_flutter/ui/app/portal_links.dart';
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
|
||||||
|
|
||||||
// Project imports:
|
// Project imports:
|
||||||
import 'package:invoiceninja_flutter/constants.dart';
|
|
||||||
import 'package:invoiceninja_flutter/data/models/models.dart';
|
import 'package:invoiceninja_flutter/data/models/models.dart';
|
||||||
import 'package:invoiceninja_flutter/ui/app/scrollable_listview.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/ui/invoice/view/invoice_view_vm.dart';
|
||||||
|
|
@ -115,29 +112,10 @@ class _InvitationListTile extends StatelessWidget {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
SizedBox(height: 8),
|
SizedBox(height: 8),
|
||||||
Row(
|
PortalLinks(
|
||||||
children: [
|
viewLink: invitation.silentLink,
|
||||||
Expanded(
|
copyLink: invitation.link,
|
||||||
child: OutlinedButton(
|
client: client,
|
||||||
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 ', ''));
|
|
||||||
},
|
|
||||||
)),
|
|
||||||
],
|
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -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/icon_message.dart';
|
||||||
import 'package:invoiceninja_flutter/ui/app/invoice/invoice_item_view.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/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/app/scrollable_listview.dart';
|
||||||
import 'package:invoiceninja_flutter/ui/invoice/view/invoice_view_vm.dart';
|
import 'package:invoiceninja_flutter/ui/invoice/view/invoice_view_vm.dart';
|
||||||
import 'package:invoiceninja_flutter/utils/formatting.dart';
|
import 'package:invoiceninja_flutter/utils/formatting.dart';
|
||||||
|
|
@ -113,6 +114,18 @@ class InvoiceOverview extends StatelessWidget {
|
||||||
ListDivider(),
|
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) {
|
if ((invoice.privateNotes ?? '').isNotEmpty) {
|
||||||
widgets.addAll([
|
widgets.addAll([
|
||||||
IconMessage(
|
IconMessage(
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue