diff --git a/lib/data/models/account_model.dart b/lib/data/models/account_model.dart index bdebd7126..a90b57747 100644 --- a/lib/data/models/account_model.dart +++ b/lib/data/models/account_model.dart @@ -11,6 +11,7 @@ abstract class AccountEntity id: '', defaultUrl: '', plan: '', + planExpires: '', latestVersion: '', currentVersion: '', reportErrors: false, @@ -34,6 +35,9 @@ abstract class AccountEntity String get plan; + @BuiltValueField(wireName: 'plan_expires') + String get planExpires; + @BuiltValueField(wireName: 'latest_version') String get latestVersion; diff --git a/lib/data/models/account_model.g.dart b/lib/data/models/account_model.g.dart index 532d124fc..57df8143a 100644 --- a/lib/data/models/account_model.g.dart +++ b/lib/data/models/account_model.g.dart @@ -26,6 +26,9 @@ class _$AccountEntitySerializer implements StructuredSerializer { specifiedType: const FullType(String)), 'plan', serializers.serialize(object.plan, specifiedType: const FullType(String)), + 'plan_expires', + serializers.serialize(object.planExpires, + specifiedType: const FullType(String)), 'latest_version', serializers.serialize(object.latestVersion, specifiedType: const FullType(String)), @@ -70,6 +73,10 @@ class _$AccountEntitySerializer implements StructuredSerializer { result.plan = serializers.deserialize(value, specifiedType: const FullType(String)) as String; break; + case 'plan_expires': + result.planExpires = serializers.deserialize(value, + specifiedType: const FullType(String)) as String; + break; case 'latest_version': result.latestVersion = serializers.deserialize(value, specifiedType: const FullType(String)) as String; @@ -95,6 +102,8 @@ class _$AccountEntity extends AccountEntity { @override final String plan; @override + final String planExpires; + @override final String latestVersion; @override final String currentVersion; @@ -107,6 +116,7 @@ class _$AccountEntity extends AccountEntity { this.defaultUrl, this.reportErrors, this.plan, + this.planExpires, this.latestVersion, this.currentVersion}) : super._() { @@ -119,6 +129,9 @@ class _$AccountEntity extends AccountEntity { if (plan == null) { throw new BuiltValueNullFieldError('AccountEntity', 'plan'); } + if (planExpires == null) { + throw new BuiltValueNullFieldError('AccountEntity', 'planExpires'); + } if (latestVersion == null) { throw new BuiltValueNullFieldError('AccountEntity', 'latestVersion'); } @@ -142,6 +155,7 @@ class _$AccountEntity extends AccountEntity { defaultUrl == other.defaultUrl && reportErrors == other.reportErrors && plan == other.plan && + planExpires == other.planExpires && latestVersion == other.latestVersion && currentVersion == other.currentVersion; } @@ -152,9 +166,11 @@ class _$AccountEntity extends AccountEntity { return __hashCode ??= $jf($jc( $jc( $jc( - $jc($jc($jc(0, id.hashCode), defaultUrl.hashCode), - reportErrors.hashCode), - plan.hashCode), + $jc( + $jc($jc($jc(0, id.hashCode), defaultUrl.hashCode), + reportErrors.hashCode), + plan.hashCode), + planExpires.hashCode), latestVersion.hashCode), currentVersion.hashCode)); } @@ -166,6 +182,7 @@ class _$AccountEntity extends AccountEntity { ..add('defaultUrl', defaultUrl) ..add('reportErrors', reportErrors) ..add('plan', plan) + ..add('planExpires', planExpires) ..add('latestVersion', latestVersion) ..add('currentVersion', currentVersion)) .toString(); @@ -192,6 +209,10 @@ class AccountEntityBuilder String get plan => _$this._plan; set plan(String plan) => _$this._plan = plan; + String _planExpires; + String get planExpires => _$this._planExpires; + set planExpires(String planExpires) => _$this._planExpires = planExpires; + String _latestVersion; String get latestVersion => _$this._latestVersion; set latestVersion(String latestVersion) => @@ -210,6 +231,7 @@ class AccountEntityBuilder _defaultUrl = _$v.defaultUrl; _reportErrors = _$v.reportErrors; _plan = _$v.plan; + _planExpires = _$v.planExpires; _latestVersion = _$v.latestVersion; _currentVersion = _$v.currentVersion; _$v = null; @@ -238,6 +260,7 @@ class AccountEntityBuilder defaultUrl: defaultUrl, reportErrors: reportErrors, plan: plan, + planExpires: planExpires, latestVersion: latestVersion, currentVersion: currentVersion); replace(_$result); diff --git a/lib/ui/app/app_header.dart b/lib/ui/app/app_header.dart new file mode 100644 index 000000000..865696fcd --- /dev/null +++ b/lib/ui/app/app_header.dart @@ -0,0 +1,85 @@ +import 'package:flutter/material.dart'; + +class AppHeader extends StatelessWidget { + const AppHeader({ + @required this.label, + @required this.value, + this.secondLabel, + this.secondValue, + }); + + final String label; + final String value; + final String secondLabel; + final String secondValue; + + @override + Widget build(BuildContext context) { + final textColor = Theme.of(context).textTheme.bodyText1.color; + + Widget _value1() { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Text(label, + style: TextStyle( + fontSize: 16.0, + color: textColor.withOpacity(.65), + )), + SizedBox( + height: 8, + ), + FittedBox( + child: Text( + (value ?? '').isEmpty ? ' ' : value, + style: TextStyle( + fontSize: 30, + ), + ), + ) + ], + ); + } + + Widget _value2() { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Text(secondLabel, + style: TextStyle( + fontSize: 16.0, + color: textColor.withOpacity(.65), + )), + SizedBox( + height: 8, + ), + FittedBox( + child: Text( + (secondValue ?? '').isEmpty ? ' ' : secondValue, + style: TextStyle( + fontSize: 30, + ), + ), + ) + ], + ); + } + + return Container( + child: Padding( + padding: EdgeInsets.only(left: 20, top: 30, right: 20, bottom: 25), + child: Row( + children: [ + Expanded(child: _value1()), + if ((secondValue ?? '').isNotEmpty) ...[ + SizedBox(width: 8), + Expanded(child: _value2()), + ], + ], + ), + ), + ); + } +} diff --git a/lib/ui/app/entity_header.dart b/lib/ui/app/entity_header.dart index c40136b0d..9d3d6adbd 100644 --- a/lib/ui/app/entity_header.dart +++ b/lib/ui/app/entity_header.dart @@ -40,7 +40,7 @@ class EntityHeader extends StatelessWidget { ), FittedBox( child: Text( - value ?? '', + (value ?? '').isEmpty ? ' ' : value, style: TextStyle( fontSize: 30, ), @@ -65,7 +65,7 @@ class EntityHeader extends StatelessWidget { ), FittedBox( child: Text( - secondValue ?? '', + (secondValue ?? '').isEmpty ? ' ' : secondValue, style: TextStyle( fontSize: 30, ), diff --git a/lib/ui/settings/account_management.dart b/lib/ui/settings/account_management.dart index c027791c7..daaf8c33f 100644 --- a/lib/ui/settings/account_management.dart +++ b/lib/ui/settings/account_management.dart @@ -6,6 +6,7 @@ import 'package:invoiceninja_flutter/data/models/entities.dart'; import 'package:invoiceninja_flutter/data/web_client.dart'; import 'package:invoiceninja_flutter/redux/app/app_state.dart'; import 'package:invoiceninja_flutter/redux/settings/settings_actions.dart'; +import 'package:invoiceninja_flutter/ui/app/app_header.dart'; import 'package:invoiceninja_flutter/ui/app/buttons/elevated_button.dart'; import 'package:invoiceninja_flutter/ui/app/form_card.dart'; import 'package:invoiceninja_flutter/ui/app/forms/app_form.dart'; @@ -13,6 +14,7 @@ import 'package:invoiceninja_flutter/ui/app/edit_scaffold.dart'; import 'package:invoiceninja_flutter/ui/app/lists/list_divider.dart'; import 'package:invoiceninja_flutter/ui/settings/account_management_vm.dart'; import 'package:invoiceninja_flutter/utils/dialogs.dart'; +import 'package:invoiceninja_flutter/utils/formatting.dart'; import 'package:invoiceninja_flutter/utils/icons.dart'; import 'package:invoiceninja_flutter/utils/localization.dart'; import 'package:url_launcher/url_launcher.dart'; @@ -125,64 +127,18 @@ class _AccountOverview extends StatelessWidget { final store = StoreProvider.of(context); final localization = AppLocalization.of(context); final state = viewModel.state; + final account = state.account; final companies = state.companies; return ListView( + shrinkWrap: true, children: [ - SizedBox(height: 14), - Padding( - padding: const EdgeInsets.all(16), - child: AppButton( - label: localization.manageTokens.toUpperCase(), - iconData: getEntityIcon(EntityType.token), - onPressed: () { - store.dispatch(ViewSettings( - navigator: Navigator.of(context), - section: kSettingsTokens, - )); - }, - ), + AppHeader( + label: localization.plan, + value: account.plan.isEmpty ? localization.free : account.plan, + secondLabel: localization.expiresOn, + secondValue: formatDate(account.planExpires, context), ), - Padding( - padding: const EdgeInsets.all(16), - child: AppButton( - label: localization.manageWebhooks.toUpperCase(), - iconData: getEntityIcon(EntityType.webhook), - onPressed: () { - store.dispatch(ViewSettings( - navigator: Navigator.of(context), - section: kSettingsWebhooks, - )); - }, - ), - ), - Padding( - padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 16), - child: ListDivider(), - ), - /* - Padding( - padding: const EdgeInsets.all(16), - child: ElevatedButton( - label: localization.purgeData.toUpperCase(), - color: Colors.red, - iconData: Icons.delete, - onPressed: () { - confirmCallback( - context: context, - message: localization.purgeDataMessage, - callback: () { - passwordCallback( - alwaysRequire: true, - context: context, - callback: (password) { - viewModel.onPurgeData(context, password); - }); - }); - }, - ), - ), - */ Padding( padding: const EdgeInsets.all(16), child: AppButton( @@ -229,6 +185,59 @@ class _AccountOverview extends StatelessWidget { padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 16), child: ListDivider(), ), + /* + Padding( + padding: const EdgeInsets.all(16), + child: ElevatedButton( + label: localization.purgeData.toUpperCase(), + color: Colors.red, + iconData: Icons.delete, + onPressed: () { + confirmCallback( + context: context, + message: localization.purgeDataMessage, + callback: () { + passwordCallback( + alwaysRequire: true, + context: context, + callback: (password) { + viewModel.onPurgeData(context, password); + }); + }); + }, + ), + ), + */ + Padding( + padding: const EdgeInsets.all(16), + child: AppButton( + label: localization.manageTokens.toUpperCase(), + iconData: getEntityIcon(EntityType.token), + onPressed: () { + store.dispatch(ViewSettings( + navigator: Navigator.of(context), + section: kSettingsTokens, + )); + }, + ), + ), + Padding( + padding: const EdgeInsets.all(16), + child: AppButton( + label: localization.manageWebhooks.toUpperCase(), + iconData: getEntityIcon(EntityType.webhook), + onPressed: () { + store.dispatch(ViewSettings( + navigator: Navigator.of(context), + section: kSettingsWebhooks, + )); + }, + ), + ), + Padding( + padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 16), + child: ListDivider(), + ), Padding( padding: const EdgeInsets.all(16), child: AppButton( diff --git a/lib/utils/i18n.dart b/lib/utils/i18n.dart index 37a9f7a45..03fd4c267 100644 --- a/lib/utils/i18n.dart +++ b/lib/utils/i18n.dart @@ -15,6 +15,9 @@ mixin LocalizationsProvider on LocaleCodeAware { static final Map> _localizedValues = { 'en': { // STARTER: lang key - do not remove comment + 'expires_on': 'Expires On', + 'free': 'Free', + 'plan': 'Plan', 'show_sidebar': 'Show Sidebar', 'hide_sidebar': 'Hide Sidebar', 'event_type': 'Event Type', @@ -38771,6 +38774,13 @@ mixin LocalizationsProvider on LocaleCodeAware { String get hideSidebar => _localizedValues[localeCode]['hide_sidebar'] ?? ''; + String get plan => _localizedValues[localeCode]['plan'] ?? ''; + + String get free => _localizedValues[localeCode]['free'] ?? ''; + + String get expiresOn => _localizedValues[localeCode]['expires_on'] ?? ''; + + String lookup(String key) { final lookupKey = toSnakeCase(key); return _localizedValues[localeCode][lookupKey] ??