diff --git a/lib/data/models/client_model.dart b/lib/data/models/client_model.dart index 881cb617b..b48dc936c 100644 --- a/lib/data/models/client_model.dart +++ b/lib/data/models/client_model.dart @@ -56,9 +56,9 @@ abstract class ClientEntity extends Object implements Built { static int counter = 0; - factory ClientEntity() { + factory ClientEntity({int id}) { return _$ClientEntity._( - id: --ClientEntity.counter, + id: id ?? --ClientEntity.counter, name: '', displayName: '', balance: 0.0, diff --git a/lib/data/models/invoice_model.dart b/lib/data/models/invoice_model.dart index 20e21e310..1b9c351d2 100644 --- a/lib/data/models/invoice_model.dart +++ b/lib/data/models/invoice_model.dart @@ -88,9 +88,9 @@ abstract class InvoiceEntity extends Object implements Built { static int counter = 0; - factory InvoiceEntity({bool isQuote = false}) { + factory InvoiceEntity({int id, bool isQuote = false}) { return _$InvoiceEntity._( - id: --InvoiceEntity.counter, + id: id ?? --InvoiceEntity.counter, amount: 0.0, balance: 0.0, clientId: 0, @@ -448,7 +448,7 @@ abstract class InvoiceEntity extends Object bool isBetween(String startDate, String endDate) { return startDate.compareTo(invoiceDate) <= 0 && - endDate.compareTo(invoiceDate) >= 0; + endDate.compareTo(invoiceDate) == 1; } double get requestedAmount => partial > 0 ? partial : amount; @@ -474,7 +474,7 @@ abstract class InvoiceEntity extends Object String get invitationDownloadLink => invitations.first?.downloadLink; PaymentEntity createPayment(CompanyEntity company) { - return PaymentEntity(company).rebuild((b) => b + return PaymentEntity(company: company).rebuild((b) => b ..invoiceId = id ..clientId = clientId ..amount = balance); diff --git a/lib/data/models/payment_model.dart b/lib/data/models/payment_model.dart index 9f4f051e5..a913a7264 100644 --- a/lib/data/models/payment_model.dart +++ b/lib/data/models/payment_model.dart @@ -56,9 +56,9 @@ abstract class PaymentEntity extends Object implements Built { static int counter = 0; - factory PaymentEntity([CompanyEntity company]) { + factory PaymentEntity({int id, CompanyEntity company}) { return _$PaymentEntity._( - id: --PaymentEntity.counter, + id: id ?? --PaymentEntity.counter, amount: 0.0, transactionReference: '', paymentDate: convertDateTimeToSqlDate(), diff --git a/lib/redux/dashboard/dashboard_selectors.dart b/lib/redux/dashboard/dashboard_selectors.dart index d65ada0b0..39d38f235 100644 --- a/lib/redux/dashboard/dashboard_selectors.dart +++ b/lib/redux/dashboard/dashboard_selectors.dart @@ -30,7 +30,8 @@ List chartOutstandingInvoices({ final Map totals = {}; invoiceMap.forEach((int, invoice) { - final client = clientMap[invoice.clientId] ?? ClientEntity(); + final client = + clientMap[invoice.clientId] ?? ClientEntity(id: invoice.clientId); final currencyId = client.currencyId > 0 ? client.currencyId : company.currencyId; @@ -39,7 +40,8 @@ List chartOutstandingInvoices({ invoice.isQuote || invoice.isRecurring) { // skip it - } else if (!invoice.isBetween(settings.startDate(company), settings.endDate(company))) { + } else if (!invoice.isBetween( + settings.startDate(company), settings.endDate(company))) { // skip it } else if (settings.currencyId > 0 && settings.currencyId != currencyId) { // skip it @@ -55,7 +57,7 @@ List chartOutstandingInvoices({ var date = DateTime.parse(settings.startDate(company)); final endDate = DateTime.parse(settings.endDate(company)); - while (! date.isAfter(endDate)) { + while (!date.isAfter(endDate)) { final key = convertDateTimeToSqlDate(date); if (totals.containsKey(key)) { data.add(ChartMoneyData(date, totals[key])); @@ -84,14 +86,17 @@ List chartPayments( final Map totals = {}; paymentMap.forEach((int, payment) { - final invoice = invoiceMap[payment.invoiceId] ?? InvoiceEntity(); - final client = clientMap[invoice.clientId] ?? ClientEntity(); + final invoice = + invoiceMap[payment.invoiceId] ?? InvoiceEntity(id: payment.invoiceId); + final client = + clientMap[invoice.clientId] ?? ClientEntity(id: invoice.clientId); final currencyId = client.currencyId > 0 ? client.currencyId : company.currencyId; if (payment.isDeleted) { // skip it - } else if (!payment.isBetween(settings.startDate(company), settings.endDate(company))) { + } else if (!payment.isBetween( + settings.startDate(company), settings.endDate(company))) { // skip it } else if (settings.currencyId > 0 && settings.currencyId != currencyId) { // skip it @@ -107,7 +112,7 @@ List chartPayments( var date = DateTime.parse(settings.startDate(company)); final endDate = DateTime.parse(settings.endDate(company)); - while (! date.isAfter(endDate)) { + while (!date.isAfter(endDate)) { final key = convertDateTimeToSqlDate(date); if (totals.containsKey(key)) { data.add(ChartMoneyData(date, totals[key])); diff --git a/lib/redux/invoice/invoice_selectors.dart b/lib/redux/invoice/invoice_selectors.dart index 6a6f8318c..56ff88ab0 100644 --- a/lib/redux/invoice/invoice_selectors.dart +++ b/lib/redux/invoice/invoice_selectors.dart @@ -29,7 +29,7 @@ List dropdownInvoiceSelector(BuiltMap invoiceMap, ClientEntity invoiceClientSelector( InvoiceEntity invoice, BuiltMap clientMap) { - return clientMap[invoice.clientId] ?? ClientEntity(); + return clientMap[invoice.clientId] ?? ClientEntity(id: invoice.clientId); } var memoizedFilteredInvoiceList = memo4( @@ -47,7 +47,7 @@ List filteredInvoicesSelector( ListUIState invoiceListState) { final list = invoiceList.where((invoiceId) { final invoice = invoiceMap[invoiceId]; - final client = clientMap[invoice.clientId] ?? ClientEntity(); + final client = clientMap[invoice.clientId] ?? ClientEntity(id: invoice.clientId); if (client == null || !client.isActive) { return false; } diff --git a/lib/redux/payment/payment_selectors.dart b/lib/redux/payment/payment_selectors.dart index 738ad9341..e0019ae83 100644 --- a/lib/redux/payment/payment_selectors.dart +++ b/lib/redux/payment/payment_selectors.dart @@ -17,13 +17,16 @@ List paymentsByInvoiceSelector(int invoiceId, } InvoiceEntity paymentInvoiceSelector(int paymentId, AppState state) { - final payment = state.paymentState.map[paymentId] ?? PaymentEntity(); - return state.invoiceState.map[payment.invoiceId] ?? InvoiceEntity(); + final payment = + state.paymentState.map[paymentId] ?? PaymentEntity(id: paymentId); + return state.invoiceState.map[payment.invoiceId] ?? + InvoiceEntity(id: payment.invoiceId); } ClientEntity paymentClientSelector(int paymentId, AppState state) { final invoice = paymentInvoiceSelector(paymentId, state); - return state.clientState.map[invoice.clientId] ?? ClientEntity(); + return state.clientState.map[invoice.clientId] ?? + ClientEntity(id: invoice.clientId); } var memoizedDropdownPaymentList = memo2( diff --git a/lib/redux/payment/payment_state.dart b/lib/redux/payment/payment_state.dart index 3b3ee479c..d02eb340b 100644 --- a/lib/redux/payment/payment_state.dart +++ b/lib/redux/payment/payment_state.dart @@ -44,7 +44,7 @@ abstract class PaymentUIState extends Object with EntityUIState implements Built factory PaymentUIState(CompanyEntity company) { return _$PaymentUIState._( listUIState: ListUIState(PaymentFields.paymentDate), - editing: PaymentEntity(company), + editing: PaymentEntity(company: company), selectedId: 0, ); } diff --git a/lib/ui/app/app_drawer.dart b/lib/ui/app/app_drawer.dart index e9ff4701d..014070808 100644 --- a/lib/ui/app/app_drawer.dart +++ b/lib/ui/app/app_drawer.dart @@ -12,13 +12,14 @@ import 'package:invoiceninja_flutter/ui/app/app_drawer_vm.dart'; import 'package:invoiceninja_flutter/ui/settings/settings_screen.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:redux/redux.dart'; -import 'package:url_launcher/url_launcher.dart'; import 'package:cached_network_image/cached_network_image.dart'; // STARTER: import - do not remove comment 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'; class AppDrawer extends StatelessWidget { final AppDrawerVM viewModel; @@ -83,8 +84,8 @@ class AppDrawer extends StatelessWidget { final ThemeData themeData = Theme.of(context); final TextStyle aboutTextStyle = themeData.textTheme.body2; - //final TextStyle linkStyle = - //themeData.textTheme.body2.copyWith(color: themeData.accentColor); + final TextStyle linkStyle = + themeData.textTheme.body2.copyWith(color: themeData.accentColor); return Drawer( child: ListView( @@ -192,7 +193,7 @@ class AppDrawer extends StatelessWidget { onCreateTap: () { navigator.pop(); store.dispatch(EditPayment( - payment: PaymentEntity(company), context: context)); + payment: PaymentEntity(company: company), context: context)); }, ), DrawerTile( @@ -217,10 +218,26 @@ class AppDrawer extends StatelessWidget { builder: (BuildContext context) => AlertDialog( semanticLabel: 'Task & Expenses', title: Text('Task & Expenses'), - content: Text( - 'Thank for your patience while we work to implement these features.\n\n' + - 'We hope to have them completed in the next few months.\n\n' - 'Until then we\'ll continue to support the legacy mobile apps.'), + content: RichText( + text: TextSpan( + children: [ + TextSpan( + style: aboutTextStyle, + text: + localization.thanksForPatience + ' ', + ), + _LinkTextSpan( + style: linkStyle, + url: getLegacyAppURL(context), + text: localization.legacyMobileApp, + ), + TextSpan( + style: aboutTextStyle, + text: '.', + ), + ], + ), + ), actions: [ FlatButton( child: Text(localization.ok.toUpperCase()), diff --git a/lib/ui/client/client_list_vm.dart b/lib/ui/client/client_list_vm.dart index f6a35182e..1405a4cb7 100644 --- a/lib/ui/client/client_list_vm.dart +++ b/lib/ui/client/client_list_vm.dart @@ -89,7 +89,7 @@ class ClientListVM { break; case EntityAction.payment: store.dispatch(EditPayment( - payment: PaymentEntity(state.selectedCompany) + payment: PaymentEntity(company: state.selectedCompany) .rebuild((b) => b.clientId = client.id), context: context)); break; diff --git a/lib/ui/client/view/client_view.dart b/lib/ui/client/view/client_view.dart index c6e864b9f..f4bc1fe17 100644 --- a/lib/ui/client/view/client_view.dart +++ b/lib/ui/client/view/client_view.dart @@ -94,7 +94,7 @@ class _ClientViewState extends State onTap: () { Navigator.of(context).pop(); store.dispatch(EditPayment( - payment: PaymentEntity(company) + payment: PaymentEntity(company: company) .rebuild((b) => b.clientId = client.id), context: context)); }, diff --git a/lib/ui/invoice/invoice_email_vm.dart b/lib/ui/invoice/invoice_email_vm.dart index f41622c9f..9e244441a 100644 --- a/lib/ui/invoice/invoice_email_vm.dart +++ b/lib/ui/invoice/invoice_email_vm.dart @@ -21,8 +21,8 @@ class InvoiceEmailScreen extends StatelessWidget { final state = store.state; final invoiceId = state.uiState.invoiceUIState.selectedId; final invoice = state.invoiceState.map[invoiceId]; - final client = - state.clientState.map[invoice.clientId] ?? ClientEntity(); + final client = state.clientState.map[invoice.clientId] ?? + ClientEntity(id: invoice.clientId); if (client.areActivitiesStale) { store.dispatch(LoadClient(clientId: client.id, loadActivities: true)); } @@ -86,7 +86,8 @@ class EmailInvoiceVM extends EmailEntityVM { isSaving: state.isSaving, company: state.selectedCompany, invoice: invoice, - client: state.clientState.map[invoice.clientId] ?? ClientEntity(), + client: state.clientState.map[invoice.clientId] ?? + ClientEntity(id: invoice.clientId), onSendPressed: (context, template, subject, body) => store.dispatch(EmailInvoiceRequest( completer: popCompleter(context, true), diff --git a/lib/ui/invoice/view/invoice_view_vm.dart b/lib/ui/invoice/view/invoice_view_vm.dart index 7e13bde02..edf7cdb08 100644 --- a/lib/ui/invoice/view/invoice_view_vm.dart +++ b/lib/ui/invoice/view/invoice_view_vm.dart @@ -116,8 +116,8 @@ class InvoiceViewVM extends EntityViewVM { factory InvoiceViewVM.fromStore(Store store) { final state = store.state; final invoice = state.invoiceState.map[state.invoiceUIState.selectedId]; - final client = - store.state.clientState.map[invoice.clientId] ?? ClientEntity(); + final client = store.state.clientState.map[invoice.clientId] ?? + ClientEntity(id: invoice.clientId); Future _handleRefresh(BuildContext context) { final completer = snackBarCompleter( diff --git a/lib/ui/payment/payment_screen.dart b/lib/ui/payment/payment_screen.dart index 8d669def7..b17b09475 100644 --- a/lib/ui/payment/payment_screen.dart +++ b/lib/ui/payment/payment_screen.dart @@ -68,7 +68,8 @@ class PaymentScreen extends StatelessWidget { backgroundColor: Theme.of(context).primaryColorDark, onPressed: () { store.dispatch(EditPayment( - payment: PaymentEntity(company), context: context)); + payment: PaymentEntity(company: company), + context: context)); }, child: Icon( Icons.add, diff --git a/lib/utils/localization.dart b/lib/utils/localization.dart index 50d0a37c2..23d4440aa 100644 --- a/lib/utils/localization.dart +++ b/lib/utils/localization.dart @@ -20,6 +20,9 @@ class AppLocalization { static final Map> _localizedValues = { 'en': { + 'thanks_for_patience': + 'Thank for your patience while we work to implement these features.\n\nWe hope to have them completed in the next few months.\n\nUntil then we\'ll continue to support the', + 'legacy_mobile_app': 'legacy mobile app', 'today': 'Today', 'custom_range': 'Custom', 'date_range': 'Date Range', @@ -8344,6 +8347,12 @@ class AppLocalization { }, }; + String get thanksForPatience => + _localizedValues[locale.toString()]['thanks_for_patience']; + + String get legacyMobileApp => + _localizedValues[locale.toString()]['legacy_mobile_app']; + String get today => _localizedValues[locale.toString()]['today']; String get customRange => _localizedValues[locale.toString()]['custom_range']; diff --git a/lib/utils/platforms.dart b/lib/utils/platforms.dart index 0d3ade8bb..9e0b04ae0 100644 --- a/lib/utils/platforms.dart +++ b/lib/utils/platforms.dart @@ -1,11 +1,15 @@ import 'package:flutter/material.dart'; -String getMapURL(BuildContext context) { - final bool iOS = Theme.of(context).platform == TargetPlatform.iOS; - return iOS - ? 'http://maps.apple.com/?address=' - : 'https://maps.google.com/?q='; -} +bool isAndroid(BuildContext context) => + Theme.of(context).platform == TargetPlatform.android; + +String getMapURL(BuildContext context) => isAndroid(context) + ? 'https://maps.google.com/?q=' + : 'http://maps.apple.com/?address='; + +String getLegacyAppURL(BuildContext context) => isAndroid(context) + ? 'https://play.google.com/store/apps/details?id=com.invoiceninja.invoiceninja' + : 'https://itunes.apple.com/WebObjects/MZStore.woa/wa/viewSoftware?id=1220337560&mt=8'; String getPlatform(BuildContext context) => - Theme.of(context).platform == TargetPlatform.iOS ? 'ios' : 'android'; \ No newline at end of file + Theme.of(context).platform == TargetPlatform.iOS ? 'ios' : 'android'; diff --git a/lib/utils/templates.dart b/lib/utils/templates.dart index d1e53c6b7..69f8f8268 100644 --- a/lib/utils/templates.dart +++ b/lib/utils/templates.dart @@ -15,7 +15,8 @@ String processTemplate( final state = StoreProvider.of(context).state; final localization = AppLocalization.of(context); final company = state.selectedCompany; - final client = state.clientState.map[invoice.clientId] ?? ClientEntity(); + final client = state.clientState.map[invoice.clientId] ?? + ClientEntity(id: invoice.clientId); final contact = client.contacts.first; final String sampleButton = '[${localization.button}]';