diff --git a/gui/packages/ubuntupro/lib/pages/subscribe_now/subscribe_now_model.dart b/gui/packages/ubuntupro/lib/pages/subscribe_now/subscribe_now_model.dart index 3eaa362ea..3b0af038d 100644 --- a/gui/packages/ubuntupro/lib/pages/subscribe_now/subscribe_now_model.dart +++ b/gui/packages/ubuntupro/lib/pages/subscribe_now/subscribe_now_model.dart @@ -4,7 +4,6 @@ import 'package:flutter/foundation.dart'; import 'package:p4w_ms_store/p4w_ms_store.dart'; import '/core/agent_api_client.dart'; -import '/core/either_value_notifier.dart'; import '/core/pro_token.dart'; class SubscribeNowModel extends ChangeNotifier { @@ -13,7 +12,7 @@ class SubscribeNowModel extends ChangeNotifier { final _token = ProTokenValue(); ProTokenValue get token => _token; - bool get canSubmit => token.valueOrNull != null; + bool get canSubmit => token.token != null; /// Returns true if the environment variable 'UP4W_ALLOW_STORE_PURCHASE' has been set. /// Since this reading won't change during the app lifetime, even if the user changes @@ -59,19 +58,23 @@ class SubscribeNowModel extends ChangeNotifier { } } -/// A value-notifier for the [ProToken] with validation. -class ProTokenValue extends EitherValueNotifier { - ProTokenValue() : super.err(TokenError.empty); +/// A [ProToken] with validation. +/// +/// Similar to a [EitherValueNotifier], without the [ValueNotifier]. +class ProTokenValue { + ProTokenValue() : either = const Either.left(TokenError.empty); - String? get token => valueOrNull?.value; - - bool get hasError => value.isLeft; + Either either; + ProToken? get token => either.orNull(); + String? get value => either.orNull()?.value; + TokenError? get error => either.fold(ifLeft: (e) => e, ifRight: (_) => null); + bool get hasError => either.isLeft; void update(String token) { - value = ProToken.create(token); + either = ProToken.create(token); } void clear() { - value = const Right(null); + either = const Either.right(null); } } diff --git a/gui/packages/ubuntupro/lib/pages/subscribe_now/subscribe_now_page.dart b/gui/packages/ubuntupro/lib/pages/subscribe_now/subscribe_now_page.dart index 24ec446fc..6de354f14 100644 --- a/gui/packages/ubuntupro/lib/pages/subscribe_now/subscribe_now_page.dart +++ b/gui/packages/ubuntupro/lib/pages/subscribe_now/subscribe_now_page.dart @@ -94,7 +94,7 @@ class SubscribeNowPage extends StatelessWidget { } void trySubmit(SubscribeNowModel model) { - model.applyProToken(model.token.valueOrNull!).then(onSubscriptionUpdate); + model.applyProToken(model.token.token!).then(onSubscriptionUpdate); model.token.clear(); controller.clear(); } diff --git a/gui/packages/ubuntupro/lib/pages/subscribe_now/subscribe_now_widgets.dart b/gui/packages/ubuntupro/lib/pages/subscribe_now/subscribe_now_widgets.dart index 2049b082c..311942a77 100644 --- a/gui/packages/ubuntupro/lib/pages/subscribe_now/subscribe_now_widgets.dart +++ b/gui/packages/ubuntupro/lib/pages/subscribe_now/subscribe_now_widgets.dart @@ -60,39 +60,29 @@ class _ProTokenInputFieldState extends State { styleSheet: linkStyle, ), const SizedBox(height: 8), - ValueListenableBuilder( - valueListenable: model.token, - builder: (context, _, __) => Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Expanded( - child: TextField( - inputFormatters: [ - // This ignores all sorts of (Unicode) whitespaces (not only at the ends). - FilteringTextInputFormatter.deny(RegExp(r'\s')), - ], - autofocus: false, - controller: widget.controller, - decoration: InputDecoration( - label: Text(lang.tokenInputHint), - error: model.token.errorOrNull?.localize(lang) != null - ? Padding( - padding: const EdgeInsets.only(top: 4), - child: Text( - model.token.errorOrNull!.localize(lang)!, - style: theme.textTheme.bodySmall!.copyWith( - color: YaruColors.of(context).error, - ), - ), - ) - : null, - ), - onChanged: model.tokenUpdate, - onSubmitted: (_) => widget.onSubmit?.call(), - ), - ), - ], + TextField( + inputFormatters: [ + // This ignores all sorts of (Unicode) whitespaces (not only at the ends). + FilteringTextInputFormatter.deny(RegExp(r'\s')), + ], + autofocus: false, + controller: widget.controller, + decoration: InputDecoration( + label: Text(lang.tokenInputHint), + error: model.token.error?.localize(lang) != null + ? Padding( + padding: const EdgeInsets.only(top: 4), + child: Text( + model.token.error!.localize(lang)!, + style: theme.textTheme.bodySmall!.copyWith( + color: YaruColors.of(context).error, + ), + ), + ) + : null, ), + onChanged: model.tokenUpdate, + onSubmitted: (_) => widget.onSubmit?.call(), ), ], ); diff --git a/gui/packages/ubuntupro/test/pages/subcribe_now/subscribe_now_widgets_test.dart b/gui/packages/ubuntupro/test/pages/subcribe_now/subscribe_now_widgets_test.dart index 89dfa3ab5..dd67a73fe 100644 --- a/gui/packages/ubuntupro/test/pages/subcribe_now/subscribe_now_widgets_test.dart +++ b/gui/packages/ubuntupro/test/pages/subcribe_now/subscribe_now_widgets_test.dart @@ -34,11 +34,11 @@ void main() { final value = ProTokenValue(); value.update(''); - expect(value.errorOrNull, TokenError.empty); + expect(value.error, TokenError.empty); for (final token in tks.invalidTokens) { value.update(token); - expect(value.errorOrNull, TokenError.invalid); + expect(value.error, TokenError.invalid); } }); test('accessors on success', () { @@ -48,23 +48,11 @@ void main() { value.update(tks.good); expect(value.hasError, isFalse); - expect(value.errorOrNull, isNull); - expect(value.token, tks.good); - expect(value.valueOrNull!.value, tks.good); - expect(value.valueOrNull, tokenInstance); - expect(value.value, equals(ProToken.create(tks.good))); - }); - - test('notify listeners', () { - final value = ProTokenValue(); - var notified = false; - value.addListener(() { - notified = true; - }); - - value.update(tks.good); - - expect(notified, isTrue); + expect(value.error, isNull); + expect(value.value, tks.good); + expect(value.value, tks.good); + expect(value.token, tokenInstance); + expect(value.either, equals(ProToken.create(tks.good))); }); });