diff --git a/lib/data/models/client_model.dart b/lib/data/models/client_model.dart index cbd0680cf..dca03c2c8 100644 --- a/lib/data/models/client_model.dart +++ b/lib/data/models/client_model.dart @@ -704,6 +704,7 @@ abstract class ContactEntity extends Object createdAt: 0, assignedUserId: '', createdUserId: '', + link: '', ); } @@ -749,6 +750,9 @@ abstract class ContactEntity extends Object @BuiltValueField(wireName: 'last_login') int get lastLogin; + @nullable // TODO remove nullable + String get link; + String get fullName { return (firstName + ' ' + lastName).trim(); } diff --git a/lib/data/models/client_model.g.dart b/lib/data/models/client_model.g.dart index 350664606..7259f3e57 100644 --- a/lib/data/models/client_model.g.dart +++ b/lib/data/models/client_model.g.dart @@ -564,6 +564,12 @@ class _$ContactEntitySerializer implements StructuredSerializer { 'id', serializers.serialize(object.id, specifiedType: const FullType(String)), ]; + if (object.link != null) { + result + ..add('link') + ..add(serializers.serialize(object.link, + specifiedType: const FullType(String))); + } if (object.isChanged != null) { result ..add('isChanged') @@ -655,6 +661,10 @@ class _$ContactEntitySerializer implements StructuredSerializer { result.lastLogin = serializers.deserialize(value, specifiedType: const FullType(int)) as int; break; + case 'link': + result.link = serializers.deserialize(value, + specifiedType: const FullType(String)) as String; + break; case 'isChanged': result.isChanged = serializers.deserialize(value, specifiedType: const FullType(bool)) as bool; @@ -1720,6 +1730,8 @@ class _$ContactEntity extends ContactEntity { @override final int lastLogin; @override + final String link; + @override final bool isChanged; @override final int createdAt; @@ -1753,6 +1765,7 @@ class _$ContactEntity extends ContactEntity { this.customValue3, this.customValue4, this.lastLogin, + this.link, this.isChanged, this.createdAt, this.updatedAt, @@ -1839,6 +1852,7 @@ class _$ContactEntity extends ContactEntity { customValue3 == other.customValue3 && customValue4 == other.customValue4 && lastLogin == other.lastLogin && + link == other.link && isChanged == other.isChanged && createdAt == other.createdAt && updatedAt == other.updatedAt && @@ -1870,18 +1884,18 @@ class _$ContactEntity extends ContactEntity { $jc( $jc( $jc( - $jc($jc($jc(0, firstName.hashCode), lastName.hashCode), - email.hashCode), - password.hashCode), - phone.hashCode), - contactKey.hashCode), - isPrimary.hashCode), - sendEmail.hashCode), - customValue1.hashCode), - customValue2.hashCode), - customValue3.hashCode), - customValue4.hashCode), - lastLogin.hashCode), + $jc($jc($jc($jc(0, firstName.hashCode), lastName.hashCode), email.hashCode), + password.hashCode), + phone.hashCode), + contactKey.hashCode), + isPrimary.hashCode), + sendEmail.hashCode), + customValue1.hashCode), + customValue2.hashCode), + customValue3.hashCode), + customValue4.hashCode), + lastLogin.hashCode), + link.hashCode), isChanged.hashCode), createdAt.hashCode), updatedAt.hashCode), @@ -1908,6 +1922,7 @@ class _$ContactEntity extends ContactEntity { ..add('customValue3', customValue3) ..add('customValue4', customValue4) ..add('lastLogin', lastLogin) + ..add('link', link) ..add('isChanged', isChanged) ..add('createdAt', createdAt) ..add('updatedAt', updatedAt) @@ -1976,6 +1991,10 @@ class ContactEntityBuilder int get lastLogin => _$this._lastLogin; set lastLogin(int lastLogin) => _$this._lastLogin = lastLogin; + String _link; + String get link => _$this._link; + set link(String link) => _$this._link = link; + bool _isChanged; bool get isChanged => _$this._isChanged; set isChanged(bool isChanged) => _$this._isChanged = isChanged; @@ -2027,6 +2046,7 @@ class ContactEntityBuilder _customValue3 = _$v.customValue3; _customValue4 = _$v.customValue4; _lastLogin = _$v.lastLogin; + _link = _$v.link; _isChanged = _$v.isChanged; _createdAt = _$v.createdAt; _updatedAt = _$v.updatedAt; @@ -2070,6 +2090,7 @@ class ContactEntityBuilder customValue3: customValue3, customValue4: customValue4, lastLogin: lastLogin, + link: link, isChanged: isChanged, createdAt: createdAt, updatedAt: updatedAt, diff --git a/lib/ui/app/lists/app_list_tile.dart b/lib/ui/app/lists/app_list_tile.dart index 55e6d9fe7..f7c05f51b 100644 --- a/lib/ui/app/lists/app_list_tile.dart +++ b/lib/ui/app/lists/app_list_tile.dart @@ -10,6 +10,7 @@ class AppListTile extends StatelessWidget { this.dense = false, this.onTap, this.copyValue, + this.buttons, }); final IconData icon; @@ -18,6 +19,7 @@ class AppListTile extends StatelessWidget { final bool dense; final Function onTap; final String copyValue; + final List buttons; @override Widget build(BuildContext context) { @@ -27,7 +29,11 @@ class AppListTile extends StatelessWidget { contentPadding: EdgeInsets.symmetric(horizontal: 25, vertical: 10), leading: Icon(icon), title: Text(title), - subtitle: subtitle == null ? Container() : Text(subtitle), + subtitle: buttons != null + ? Row( + children: buttons, + ) + : (subtitle == null ? Container() : Text(subtitle)), dense: dense, onTap: onTap, onLongPress: () { diff --git a/lib/ui/client/view/client_view_details.dart b/lib/ui/client/view/client_view_details.dart index 736b68c7f..738253a5f 100644 --- a/lib/ui/client/view/client_view_details.dart +++ b/lib/ui/client/view/client_view_details.dart @@ -1,6 +1,8 @@ import 'dart:async'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +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/utils/formatting.dart'; @@ -50,10 +52,34 @@ class _ClientViewDetailsState extends State { contacts.forEach((contact) { if ((contact.email ?? '').isNotEmpty) { listTiles.add(AppListTile( + buttons: [ + Expanded( + child: OutlineButton( + child: Text(localization.viewPortal.toUpperCase()), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(5)), + onPressed: () { + launch('${contact.link}&client_hash=${client.clientHash}', + forceWebView: false, forceSafariVC: false); + }, + )), + SizedBox(width: kTableColumnGap), + Expanded( + child: OutlineButton( + child: Text(localization.copyLink.toUpperCase()), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(5)), + onPressed: () { + Clipboard.setData(ClipboardData(text: contact.link)); + Scaffold.of(context).showSnackBar(SnackBar( + content: Text(localization.copiedToClipboard + .replaceFirst(':value ', '')))); + }, + )), + ], icon: Icons.email, title: contact.fullName + '\n' + contact.email, copyValue: contact.email, - subtitle: localization.email, onTap: () => setState(() { _launched = _launchURL(context, 'mailto:' + contact.email); }),