diff --git a/lib/data/models/company_model.dart b/lib/data/models/company_model.dart index dfe9c0127..9015bdee6 100644 --- a/lib/data/models/company_model.dart +++ b/lib/data/models/company_model.dart @@ -100,6 +100,7 @@ abstract class CompanyEntity extends Object passwordTimeout: 30 * 60 * 1000, oauthPasswordRequired: false, markdownEnabled: true, + markdownEmailEnabled: true, useCommaAsDecimalPlace: false, reportIncludeDrafts: false, groups: BuiltList(), @@ -243,6 +244,9 @@ abstract class CompanyEntity extends Object @BuiltValueField(wireName: 'markdown_enabled') bool get markdownEnabled; + @BuiltValueField(wireName: 'markdown_email_enabled') + bool get markdownEmailEnabled; + @BuiltValueField(wireName: 'use_comma_as_decimal_place') bool get useCommaAsDecimalPlace; @@ -590,6 +594,7 @@ abstract class CompanyEntity extends Object ..invoiceTaskDatelog = true ..showTaskEndDate = false ..markdownEnabled = true + ..markdownEmailEnabled = true ..useCommaAsDecimalPlace = false ..reportIncludeDrafts = false ..convertRateToClient = true diff --git a/lib/data/models/company_model.g.dart b/lib/data/models/company_model.g.dart index 7d35d0bea..93d36fa5b 100644 --- a/lib/data/models/company_model.g.dart +++ b/lib/data/models/company_model.g.dart @@ -132,6 +132,9 @@ class _$CompanyEntitySerializer implements StructuredSerializer { 'markdown_enabled', serializers.serialize(object.markdownEnabled, specifiedType: const FullType(bool)), + 'markdown_email_enabled', + serializers.serialize(object.markdownEmailEnabled, + specifiedType: const FullType(bool)), 'use_comma_as_decimal_place', serializers.serialize(object.useCommaAsDecimalPlace, specifiedType: const FullType(bool)), @@ -493,6 +496,10 @@ class _$CompanyEntitySerializer implements StructuredSerializer { result.markdownEnabled = serializers.deserialize(value, specifiedType: const FullType(bool)) as bool; break; + case 'markdown_email_enabled': + result.markdownEmailEnabled = serializers.deserialize(value, + specifiedType: const FullType(bool)) as bool; + break; case 'use_comma_as_decimal_place': result.useCommaAsDecimalPlace = serializers.deserialize(value, specifiedType: const FullType(bool)) as bool; @@ -1414,6 +1421,8 @@ class _$CompanyEntity extends CompanyEntity { @override final bool markdownEnabled; @override + final bool markdownEmailEnabled; + @override final bool useCommaAsDecimalPlace; @override final bool reportIncludeDrafts; @@ -1559,6 +1568,7 @@ class _$CompanyEntity extends CompanyEntity { this.passwordTimeout, this.oauthPasswordRequired, this.markdownEnabled, + this.markdownEmailEnabled, this.useCommaAsDecimalPlace, this.reportIncludeDrafts, this.groups, @@ -1678,6 +1688,8 @@ class _$CompanyEntity extends CompanyEntity { oauthPasswordRequired, 'CompanyEntity', 'oauthPasswordRequired'); BuiltValueNullFieldError.checkNotNull( markdownEnabled, 'CompanyEntity', 'markdownEnabled'); + BuiltValueNullFieldError.checkNotNull( + markdownEmailEnabled, 'CompanyEntity', 'markdownEmailEnabled'); BuiltValueNullFieldError.checkNotNull( useCommaAsDecimalPlace, 'CompanyEntity', 'useCommaAsDecimalPlace'); BuiltValueNullFieldError.checkNotNull( @@ -1812,6 +1824,7 @@ class _$CompanyEntity extends CompanyEntity { passwordTimeout == other.passwordTimeout && oauthPasswordRequired == other.oauthPasswordRequired && markdownEnabled == other.markdownEnabled && + markdownEmailEnabled == other.markdownEmailEnabled && useCommaAsDecimalPlace == other.useCommaAsDecimalPlace && reportIncludeDrafts == other.reportIncludeDrafts && groups == other.groups && @@ -1889,7 +1902,7 @@ class _$CompanyEntity extends CompanyEntity { $jc( $jc( $jc( - $jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc(0, enableCustomSurchargeTaxes1.hashCode), enableCustomSurchargeTaxes2.hashCode), enableCustomSurchargeTaxes3.hashCode), enableCustomSurchargeTaxes4.hashCode), sizeId.hashCode), industryId.hashCode), subdomain.hashCode), portalMode.hashCode), portalDomain.hashCode), updateProducts.hashCode), convertProductExchangeRate.hashCode), convertRateToClient.hashCode), fillProducts.hashCode), enableProductCost.hashCode), enableProductQuantity.hashCode), enableProductDiscount.hashCode), defaultTaskIsDateBased.hashCode), defaultQuantity.hashCode), showProductDetails.hashCode), clientCanRegister.hashCode), isLarge.hashCode), isDisabled.hashCode), enableShopApi.hashCode), companyKey.hashCode), firstDayOfWeek.hashCode), firstMonthOfYear.hashCode), numberOfInvoiceTaxRates.hashCode), numberOfItemTaxRates.hashCode), expenseInclusiveTaxes.hashCode), sessionTimeout.hashCode), passwordTimeout.hashCode), oauthPasswordRequired.hashCode), markdownEnabled.hashCode), useCommaAsDecimalPlace.hashCode), reportIncludeDrafts.hashCode), groups.hashCode), activities.hashCode), taxRates.hashCode), taskStatuses.hashCode), taskStatusMap.hashCode), companyGateways.hashCode), expenseCategories.hashCode), users.hashCode), clients.hashCode), products.hashCode), invoices.hashCode), recurringInvoices.hashCode), recurringExpenses.hashCode), payments.hashCode), quotes.hashCode), credits.hashCode), tasks.hashCode), projects.hashCode), expenses.hashCode), vendors.hashCode), designs.hashCode), documents.hashCode), tokens.hashCode), webhooks.hashCode), subscriptions.hashCode), paymentTerms.hashCode), systemLogs.hashCode), clientRegistrationFields.hashCode), customFields.hashCode), slackWebhookUrl.hashCode), googleAnalyticsKey.hashCode), markExpensesInvoiceable.hashCode), markExpensesPaid.hashCode), + $jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc(0, enableCustomSurchargeTaxes1.hashCode), enableCustomSurchargeTaxes2.hashCode), enableCustomSurchargeTaxes3.hashCode), enableCustomSurchargeTaxes4.hashCode), sizeId.hashCode), industryId.hashCode), subdomain.hashCode), portalMode.hashCode), portalDomain.hashCode), updateProducts.hashCode), convertProductExchangeRate.hashCode), convertRateToClient.hashCode), fillProducts.hashCode), enableProductCost.hashCode), enableProductQuantity.hashCode), enableProductDiscount.hashCode), defaultTaskIsDateBased.hashCode), defaultQuantity.hashCode), showProductDetails.hashCode), clientCanRegister.hashCode), isLarge.hashCode), isDisabled.hashCode), enableShopApi.hashCode), companyKey.hashCode), firstDayOfWeek.hashCode), firstMonthOfYear.hashCode), numberOfInvoiceTaxRates.hashCode), numberOfItemTaxRates.hashCode), expenseInclusiveTaxes.hashCode), sessionTimeout.hashCode), passwordTimeout.hashCode), oauthPasswordRequired.hashCode), markdownEnabled.hashCode), markdownEmailEnabled.hashCode), useCommaAsDecimalPlace.hashCode), reportIncludeDrafts.hashCode), groups.hashCode), activities.hashCode), taxRates.hashCode), taskStatuses.hashCode), taskStatusMap.hashCode), companyGateways.hashCode), expenseCategories.hashCode), users.hashCode), clients.hashCode), products.hashCode), invoices.hashCode), recurringInvoices.hashCode), recurringExpenses.hashCode), payments.hashCode), quotes.hashCode), credits.hashCode), tasks.hashCode), projects.hashCode), expenses.hashCode), vendors.hashCode), designs.hashCode), documents.hashCode), tokens.hashCode), webhooks.hashCode), subscriptions.hashCode), paymentTerms.hashCode), systemLogs.hashCode), clientRegistrationFields.hashCode), customFields.hashCode), slackWebhookUrl.hashCode), googleAnalyticsKey.hashCode), markExpensesInvoiceable.hashCode), markExpensesPaid.hashCode), invoiceExpenseDocuments.hashCode), invoiceTaskDocuments.hashCode), invoiceTaskTimelog.hashCode), @@ -1947,6 +1960,7 @@ class _$CompanyEntity extends CompanyEntity { ..add('passwordTimeout', passwordTimeout) ..add('oauthPasswordRequired', oauthPasswordRequired) ..add('markdownEnabled', markdownEnabled) + ..add('markdownEmailEnabled', markdownEmailEnabled) ..add('useCommaAsDecimalPlace', useCommaAsDecimalPlace) ..add('reportIncludeDrafts', reportIncludeDrafts) ..add('groups', groups) @@ -2165,6 +2179,11 @@ class CompanyEntityBuilder set markdownEnabled(bool markdownEnabled) => _$this._markdownEnabled = markdownEnabled; + bool _markdownEmailEnabled; + bool get markdownEmailEnabled => _$this._markdownEmailEnabled; + set markdownEmailEnabled(bool markdownEmailEnabled) => + _$this._markdownEmailEnabled = markdownEmailEnabled; + bool _useCommaAsDecimalPlace; bool get useCommaAsDecimalPlace => _$this._useCommaAsDecimalPlace; set useCommaAsDecimalPlace(bool useCommaAsDecimalPlace) => @@ -2490,6 +2509,7 @@ class CompanyEntityBuilder _passwordTimeout = $v.passwordTimeout; _oauthPasswordRequired = $v.oauthPasswordRequired; _markdownEnabled = $v.markdownEnabled; + _markdownEmailEnabled = $v.markdownEmailEnabled; _useCommaAsDecimalPlace = $v.useCommaAsDecimalPlace; _reportIncludeDrafts = $v.reportIncludeDrafts; _groups = $v.groups.toBuilder(); @@ -2613,6 +2633,7 @@ class CompanyEntityBuilder passwordTimeout: BuiltValueNullFieldError.checkNotNull(passwordTimeout, 'CompanyEntity', 'passwordTimeout'), oauthPasswordRequired: BuiltValueNullFieldError.checkNotNull(oauthPasswordRequired, 'CompanyEntity', 'oauthPasswordRequired'), markdownEnabled: BuiltValueNullFieldError.checkNotNull(markdownEnabled, 'CompanyEntity', 'markdownEnabled'), + markdownEmailEnabled: BuiltValueNullFieldError.checkNotNull(markdownEmailEnabled, 'CompanyEntity', 'markdownEmailEnabled'), useCommaAsDecimalPlace: BuiltValueNullFieldError.checkNotNull(useCommaAsDecimalPlace, 'CompanyEntity', 'useCommaAsDecimalPlace'), reportIncludeDrafts: BuiltValueNullFieldError.checkNotNull(reportIncludeDrafts, 'CompanyEntity', 'reportIncludeDrafts'), groups: groups.build(), diff --git a/lib/ui/app/invoice/invoice_email_view.dart b/lib/ui/app/invoice/invoice_email_view.dart index f8692404e..c2654a339 100644 --- a/lib/ui/app/invoice/invoice_email_view.dart +++ b/lib/ui/app/invoice/invoice_email_view.dart @@ -1,5 +1,6 @@ // Flutter imports: import 'package:flutter/material.dart'; +import 'package:html2md/html2md.dart' as html2md; // Project imports: import 'package:invoiceninja_flutter/constants.dart'; @@ -147,6 +148,12 @@ class _InvoiceEmailViewState extends State _bodyPreview = body.trim(); _rawBodyPreview = rawBody.trim(); + final company = widget.viewModel.state.company; + if (company.markdownEmailEnabled && + _rawBodyPreview.startsWith('

')) { + _rawBodyPreview = html2md.convert(_rawBodyPreview); + } + if (origSubject.isEmpty && origBody.isEmpty) { _subjectController.text = rawSubject.trim(); _bodyController.text = rawBody.trim(); @@ -292,19 +299,7 @@ class _InvoiceEmailViewState extends State enabled: enableCustomEmail, ), ), - if ((_rawBodyPreview ?? '').startsWith('

')) - Padding( - padding: const EdgeInsets.symmetric(horizontal: 20), - child: DecoratedFormField( - controller: _bodyController, - label: localization.body, - maxLines: enableCustomEmail ? 6 : 2, - keyboardType: TextInputType.multiline, - onChanged: (_) => _onChanged(), - enabled: enableCustomEmail, - ), - ) - else + if (state.company.markdownEmailEnabled) Expanded( child: Material( color: Colors.white, @@ -324,7 +319,19 @@ class _InvoiceEmailViewState extends State ], ), ), - ), + ) + else + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20), + child: DecoratedFormField( + controller: _bodyController, + label: localization.body, + maxLines: enableCustomEmail ? 6 : 2, + keyboardType: TextInputType.multiline, + onChanged: (_) => _onChanged(), + enabled: enableCustomEmail, + ), + ) ], ); } diff --git a/pubspec.foss.yaml b/pubspec.foss.yaml index 3c769bdcf..295bdc28a 100644 --- a/pubspec.foss.yaml +++ b/pubspec.foss.yaml @@ -56,9 +56,10 @@ dependencies: boardview: ^0.2.2 pointer_interceptor: ^0.9.0 contacts_service: ^0.6.1 - super_editor: ^0.1.0 diacritic: ^0.1.3 states_rebuilder: ^5.2.0 + super_editor: ^0.2.0 + html2md: ^1.2.5 # bitsdojo_window: ^0.1.1+1 # printing: ^5.6.3 # quick_actions: ^0.2.1 diff --git a/pubspec.lock b/pubspec.lock index 9c816f361..4de878c21 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -225,6 +225,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "3.0.1" + csslib: + dependency: transitive + description: + name: csslib + url: "https://pub.dartlang.org" + source: hosted + version: "0.17.1" dart_style: dependency: transitive description: @@ -472,6 +479,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.0" + html: + dependency: transitive + description: + name: html + url: "https://pub.dartlang.org" + source: hosted + version: "0.15.0" + html2md: + dependency: "direct main" + description: + name: html2md + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.5" http: dependency: "direct main" description: diff --git a/pubspec.next.yaml b/pubspec.next.yaml index ec5d496a5..0a3168cd9 100644 --- a/pubspec.next.yaml +++ b/pubspec.next.yaml @@ -56,9 +56,10 @@ dependencies: boardview: ^0.2.2 pointer_interceptor: ^0.9.0 contacts_service: ^0.6.1 - super_editor: ^0.1.0 diacritic: ^0.1.3 states_rebuilder: ^5.2.0 + super_editor: ^0.2.0 + html2md: ^1.2.5 # bitsdojo_window: ^0.1.1+1 # printing: ^5.6.3 # quick_actions: ^0.2.1 diff --git a/pubspec.yaml b/pubspec.yaml index b7d0dc14b..b66bf41e8 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -59,6 +59,7 @@ dependencies: diacritic: ^0.1.3 states_rebuilder: ^5.2.0 super_editor: ^0.2.0 + html2md: ^1.2.5 # bitsdojo_window: ^0.1.1+1 # printing: ^5.6.3 # quick_actions: ^0.2.1