From c1906a643d8ad4376ce2f9f76335199dffa5390c Mon Sep 17 00:00:00 2001 From: Ethan Lee <125412902+ethan-tbd@users.noreply.github.com> Date: Mon, 24 Jun 2024 10:52:26 -0700 Subject: [PATCH] feat: dap send page (#209) --- .../account/account_balance_card.dart | 59 +++++----- lib/features/account/account_page.dart | 14 +-- lib/features/did/did_form.dart | 2 +- lib/features/feature_flags/feature_flag.dart | 5 + lib/features/send/send_details_page.dart | 58 ---------- lib/features/send/send_page.dart | 106 ++++++------------ lib/l10n/app_en.arb | 4 +- lib/l10n/app_localizations.dart | 12 +- lib/l10n/app_localizations_en.dart | 4 +- lib/main.dart | 5 +- test/features/pfis/pfis_add_page_test.dart | 2 +- .../features/send/send_details_page_test.dart | 45 -------- test/features/send/send_page_test.dart | 97 +++------------- 13 files changed, 104 insertions(+), 309 deletions(-) delete mode 100644 lib/features/send/send_details_page.dart delete mode 100644 test/features/send/send_details_page_test.dart diff --git a/lib/features/account/account_balance_card.dart b/lib/features/account/account_balance_card.dart index 06a17266..f3917309 100644 --- a/lib/features/account/account_balance_card.dart +++ b/lib/features/account/account_balance_card.dart @@ -18,7 +18,7 @@ class AccountBalanceCard extends HookConsumerWidget { final pfis = ref.watch(pfisProvider); final accountBalance = ref.watch(accountBalanceProvider); - final accountTotal = accountBalance.asData?.value?.total ?? ''; + final accountTotal = accountBalance.asData?.value?.total ?? '0'; final accountCurrency = accountBalance.asData?.value?.currencyCode ?? ''; AccountBalanceNotifier getAccountBalanceNotifier() => @@ -70,35 +70,36 @@ class AccountBalanceCard extends HookConsumerWidget { BuildContext context, String accountTotal, String accountCurrency, - ) => - Center( - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.baseline, - textBaseline: TextBaseline.alphabetic, - children: [ - Flexible( - child: AutoSizeText( - accountTotal, - style: Theme.of(context).textTheme.displayMedium?.copyWith( - fontWeight: FontWeight.bold, - ), - maxLines: 1, - ), + ) { + return Center( + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.baseline, + textBaseline: TextBaseline.alphabetic, + children: [ + Flexible( + child: AutoSizeText( + accountTotal, + style: Theme.of(context).textTheme.displayMedium?.copyWith( + fontWeight: FontWeight.bold, + ), + maxLines: 1, ), - const SizedBox(width: Grid.half), - Padding( - padding: const EdgeInsets.symmetric(horizontal: Grid.xxs), - child: Text( - accountCurrency, - style: Theme.of(context).textTheme.headlineMedium?.copyWith( - fontWeight: FontWeight.bold, - ), - ), + ), + const SizedBox(width: Grid.half), + Padding( + padding: const EdgeInsets.symmetric(horizontal: Grid.xxs), + child: Text( + accountCurrency, + style: Theme.of(context).textTheme.headlineMedium?.copyWith( + fontWeight: FontWeight.bold, + ), ), - ], - ), - ); + ), + ], + ), + ); + } Widget _buildDepositWithdrawButtons(BuildContext context) => Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, @@ -112,6 +113,7 @@ class AccountBalanceCard extends HookConsumerWidget { transactionType: TransactionType.deposit, ), ), + fullscreenDialog: true, ), ), child: Text(Loc.of(context).deposit), @@ -127,6 +129,7 @@ class AccountBalanceCard extends HookConsumerWidget { transactionType: TransactionType.withdraw, ), ), + fullscreenDialog: true, ), ), child: Text(Loc.of(context).withdraw), diff --git a/lib/features/account/account_page.dart b/lib/features/account/account_page.dart index c52d45ed..f8ffae58 100644 --- a/lib/features/account/account_page.dart +++ b/lib/features/account/account_page.dart @@ -84,14 +84,12 @@ class AccountPage extends HookConsumerWidget { ), child: IconButton( icon: const Icon(Icons.qr_code, size: Grid.sm), - onPressed: () { - Navigator.of(context).push( - MaterialPageRoute( - builder: (_) => DidQrTabs(dap: dap), - fullscreenDialog: true, - ), - ); - }, + onPressed: () => Navigator.of(context).push( + MaterialPageRoute( + builder: (_) => DidQrTabs(dap: dap), + fullscreenDialog: true, + ), + ), ), ), ), diff --git a/lib/features/did/did_form.dart b/lib/features/did/did_form.dart index 6750afbd..f6f6fc2b 100644 --- a/lib/features/did/did_form.dart +++ b/lib/features/did/did_form.dart @@ -64,7 +64,7 @@ class DidForm extends HookConsumerWidget { ), ), DidQrTile( - title: Loc.of(context).dontKnowTheirDid, + title: Loc.of(context).dontKnowTheirDap, didTextController: textController, errorText: errorText, ), diff --git a/lib/features/feature_flags/feature_flag.dart b/lib/features/feature_flags/feature_flag.dart index 63c55c22..899cf0e3 100644 --- a/lib/features/feature_flags/feature_flag.dart +++ b/lib/features/feature_flags/feature_flag.dart @@ -35,3 +35,8 @@ const lucidMode = FeatureFlag( name: 'Lucid mode', description: 'Access all your PFI offerings', ); + +const remittance = FeatureFlag( + name: 'Remittance', + description: 'Experience a tranditional remittance flow', +); diff --git a/lib/features/send/send_details_page.dart b/lib/features/send/send_details_page.dart deleted file mode 100644 index 0b882687..00000000 --- a/lib/features/send/send_details_page.dart +++ /dev/null @@ -1,58 +0,0 @@ -import 'package:didpay/features/did/did_form.dart'; -import 'package:didpay/l10n/app_localizations.dart'; -import 'package:didpay/shared/confirmation_message.dart'; -import 'package:didpay/shared/error_message.dart'; -import 'package:didpay/shared/header.dart'; -import 'package:didpay/shared/loading_message.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_hooks/flutter_hooks.dart'; -import 'package:hooks_riverpod/hooks_riverpod.dart'; - -class SendDetailsPage extends HookConsumerWidget { - final String sendAmount; - - const SendDetailsPage({required this.sendAmount, super.key}); - - @override - Widget build(BuildContext context, WidgetRef ref) { - final send = useState?>(null); - - return Scaffold( - appBar: AppBar(), - body: SafeArea( - child: send.value != null - ? send.value!.when( - data: (_) => ConfirmationMessage( - message: Loc.of(context).yourPaymentWasSent,), - loading: () => - LoadingMessage(message: Loc.of(context).sendingPayment), - error: (error, _) => ErrorMessage( - message: error.toString(), - onRetry: () => _sendPayment(send), - ), - ) - : Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Header( - title: Loc.of(context).enterRecipientDid, - subtitle: Loc.of(context).makeSureInfoIsCorrect, - ), - Expanded( - child: DidForm( - buttonTitle: Loc.of(context).sendAmountUsdc(sendAmount), - onSubmit: (did) => _sendPayment(send), - ), - ), - ], - ), - ), - ); - } - - Future _sendPayment(ValueNotifier?> state) async { - state.value = const AsyncLoading(); - await Future.delayed(const Duration(milliseconds: 1000)); - state.value = const AsyncValue.data(null); - } -} diff --git a/lib/features/send/send_page.dart b/lib/features/send/send_page.dart index 07fa7b97..4e9d9174 100644 --- a/lib/features/send/send_page.dart +++ b/lib/features/send/send_page.dart @@ -1,17 +1,15 @@ -import 'package:didpay/features/account/account_balance_notifier.dart'; import 'package:didpay/features/countries/countries_page.dart'; +import 'package:didpay/features/did/did_form.dart'; import 'package:didpay/features/feature_flags/feature_flag.dart'; import 'package:didpay/features/feature_flags/feature_flags_notifier.dart'; import 'package:didpay/features/feature_flags/lucid/lucid_offerings_page.dart'; -import 'package:didpay/features/send/send_details_page.dart'; +import 'package:didpay/features/payment/payment_amount_page.dart'; +import 'package:didpay/features/payment/payment_state.dart'; +import 'package:didpay/features/transaction/transaction.dart'; import 'package:didpay/l10n/app_localizations.dart'; -import 'package:didpay/shared/next_button.dart'; -import 'package:didpay/shared/number/number_display.dart'; -import 'package:didpay/shared/number/number_key_press.dart'; -import 'package:didpay/shared/number/number_pad.dart'; +import 'package:didpay/shared/header.dart'; import 'package:didpay/shared/theme/grid.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; class SendPage extends HookConsumerWidget { @@ -19,64 +17,33 @@ class SendPage extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final accountBalance = ref.watch(accountBalanceProvider); final featureFlags = ref.watch(featureFlagsProvider); - final amount = useState('0'); - final keyPress = useState(NumberKeyPress(0, '')); - - final sendCurrency = accountBalance.asData?.value?.currencyCode ?? ''; - return Scaffold( appBar: _buildAppBar(context, featureFlags), body: SafeArea( child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ + Header( + title: Loc.of(context).enterRecipientDap, + subtitle: Loc.of(context).makeSureInfoIsCorrect, + ), Expanded( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Padding( - padding: - const EdgeInsets.symmetric(horizontal: Grid.side), - child: NumberDisplay( - currencyCode: sendCurrency, - currencyWidget: _buildCurrency(context, sendCurrency), - amount: amount, - keyPress: keyPress, - textStyle: const TextStyle( - fontSize: 80, - fontWeight: FontWeight.bold, - ), - ), + child: DidForm( + buttonTitle: Loc.of(context).next, + onSubmit: (did) => Navigator.of(context).push( + MaterialPageRoute( + builder: (_) => const PaymentAmountPage( + paymentState: PaymentState( + transactionType: TransactionType.send, ), - ], + ), + fullscreenDialog: true, ), - ], - ), - ), - Padding( - padding: const EdgeInsets.symmetric(vertical: Grid.xs), - child: NumberPad( - onKeyPressed: (key) => keyPress.value = - NumberKeyPress(keyPress.value.count + 1, key), + ), ), ), - NextButton( - onPressed: double.tryParse(amount.value) == 0 - ? null - : () => Navigator.of(context).push( - MaterialPageRoute( - builder: (context) => - SendDetailsPage(sendAmount: amount.value), - ), - ), - title: Loc.of(context).send, - ), ], ), ), @@ -85,19 +52,20 @@ class SendPage extends HookConsumerWidget { AppBar _buildAppBar(BuildContext context, List featureFlags) => AppBar( - leading: Padding( - padding: const EdgeInsets.only(left: Grid.xxs), - child: IconButton( - icon: const Icon(Icons.language, size: Grid.lg), - onPressed: () { - Navigator.of(context).push( - MaterialPageRoute( - builder: (context) => const CountriesPage(), + leading: featureFlags + .any((flag) => flag.name == 'Remittance' && flag.isEnabled) + ? Padding( + padding: const EdgeInsets.only(left: Grid.xxs), + child: IconButton( + icon: const Icon(Icons.language, size: Grid.lg), + onPressed: () => Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => const CountriesPage(), + ), + ), ), - ); - }, - ), - ), + ) + : null, actions: featureFlags.any( (flag) => flag.name == Loc.of(context).lucidMode && flag.isEnabled, ) @@ -116,14 +84,4 @@ class SendPage extends HookConsumerWidget { ] : null, ); - - Widget _buildCurrency(BuildContext context, String sendCurrency) => Padding( - padding: const EdgeInsets.symmetric(horizontal: Grid.xxs), - child: Text( - sendCurrency, - style: Theme.of(context).textTheme.displayMedium?.copyWith( - fontWeight: FontWeight.bold, - ), - ), - ); } diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index f64d5830..2d806021 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -131,7 +131,7 @@ "cancel": "Cancel", "linkedPfis": "Linked PFIs", "noTransactionsFound": "No transactions found", - "enterRecipientDid": "Enter the recipient's DID", + "enterRecipientDap": "Enter the recipient's DAP", "fetchingOfferings": "Fetching offerings...", "startingIdv": "Starting identity verification...", "verifyingYourIdentity": "Verifying your identity...", @@ -144,7 +144,7 @@ "fetchingTransactions": "Fetching transactions...", "scan": "Scan", "cameraUnavailable": "Camera unavailable", - "dontKnowTheirDid": "Don't know their DID? Scan their DID QR code instead", + "dontKnowTheirDap": "Don't know their DAP? Scan their QR code instead", "featureFlags": "Feature flags", "lucidMode": "Lucid mode", "selectFromUnfilteredList": "Select from an unfiltered list of all your PFI offerings.", diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart index 24b88df6..ffdf02d9 100644 --- a/lib/l10n/app_localizations.dart +++ b/lib/l10n/app_localizations.dart @@ -697,11 +697,11 @@ abstract class Loc { /// **'No transactions found'** String get noTransactionsFound; - /// No description provided for @enterRecipientDid. + /// No description provided for @enterRecipientDap. /// /// In en, this message translates to: - /// **'Enter the recipient\'s DID'** - String get enterRecipientDid; + /// **'Enter the recipient\'s DAP'** + String get enterRecipientDap; /// No description provided for @fetchingOfferings. /// @@ -769,11 +769,11 @@ abstract class Loc { /// **'Camera unavailable'** String get cameraUnavailable; - /// No description provided for @dontKnowTheirDid. + /// No description provided for @dontKnowTheirDap. /// /// In en, this message translates to: - /// **'Don\'t know their DID? Scan their DID QR code instead'** - String get dontKnowTheirDid; + /// **'Don\'t know their DAP? Scan their QR code instead'** + String get dontKnowTheirDap; /// No description provided for @featureFlags. /// diff --git a/lib/l10n/app_localizations_en.dart b/lib/l10n/app_localizations_en.dart index 1e50a3a9..08c9dadf 100644 --- a/lib/l10n/app_localizations_en.dart +++ b/lib/l10n/app_localizations_en.dart @@ -316,7 +316,7 @@ class LocEn extends Loc { String get noTransactionsFound => 'No transactions found'; @override - String get enterRecipientDid => 'Enter the recipient\'s DID'; + String get enterRecipientDap => 'Enter the recipient\'s DAP'; @override String get fetchingOfferings => 'Fetching offerings...'; @@ -352,7 +352,7 @@ class LocEn extends Loc { String get cameraUnavailable => 'Camera unavailable'; @override - String get dontKnowTheirDid => 'Don\'t know their DID? Scan their DID QR code instead'; + String get dontKnowTheirDap => 'Don\'t know their DAP? Scan their QR code instead'; @override String get featureFlags => 'Feature flags'; diff --git a/lib/main.dart b/lib/main.dart index 6fbb63ef..36e94eb1 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -63,7 +63,10 @@ Future> notifierOverrides() async { final featureFlagsNotifier = await FeatureFlagsNotifier.create(featureFlagsBox); - if (featureFlagsBox.isEmpty) await featureFlagsNotifier.add(lucidMode); + if (featureFlagsBox.isEmpty) { + await featureFlagsNotifier.add(remittance); + await featureFlagsNotifier.add(lucidMode); + } return [ pfisProvider.overrideWith((ref) => pfisNofitier), diff --git a/test/features/pfis/pfis_add_page_test.dart b/test/features/pfis/pfis_add_page_test.dart index 182af049..7b09c07d 100644 --- a/test/features/pfis/pfis_add_page_test.dart +++ b/test/features/pfis/pfis_add_page_test.dart @@ -17,7 +17,7 @@ void main() { expect( find.widgetWithText( ListTile, - "Don't know their DID? Scan their DID QR code instead", + "Don't know their DAP? Scan their QR code instead", ), findsOneWidget, ); diff --git a/test/features/send/send_details_page_test.dart b/test/features/send/send_details_page_test.dart deleted file mode 100644 index 018f876a..00000000 --- a/test/features/send/send_details_page_test.dart +++ /dev/null @@ -1,45 +0,0 @@ -import 'package:didpay/features/did/did_provider.dart'; -import 'package:didpay/features/send/send_details_page.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; - -import '../../helpers/test_data.dart'; -import '../../helpers/widget_helpers.dart'; - -void main() async { - await TestData.initializeDids(); - - final did = TestData.aliceDid; - group('SendDetailsPage', () { - Widget sendDetailsPageTestWidget() => WidgetHelpers.testableWidget( - child: const SendDetailsPage(sendAmount: '25'), - overrides: [ - didProvider.overrideWithValue(did), - ], - ); - - testWidgets('should show QR Code CTA', (tester) async { - await tester.pumpWidget(sendDetailsPageTestWidget()); - - expect( - find.widgetWithText( - ListTile, - "Don't know their DID? Scan their DID QR code instead", - ), - findsOneWidget, - ); - }); - - testWidgets('should show input field', (tester) async { - await tester.pumpWidget(sendDetailsPageTestWidget()); - - expect(find.byType(TextField), findsOneWidget); - }); - - testWidgets('should show send button', (tester) async { - await tester.pumpWidget(sendDetailsPageTestWidget()); - - expect(find.widgetWithText(FilledButton, 'Send 25 USDC'), findsOneWidget); - }); - }); -} diff --git a/test/features/send/send_page_test.dart b/test/features/send/send_page_test.dart index 13121e8d..efd6025b 100644 --- a/test/features/send/send_page_test.dart +++ b/test/features/send/send_page_test.dart @@ -1,12 +1,6 @@ -import 'package:auto_size_text/auto_size_text.dart'; -import 'package:didpay/features/account/account_balance_notifier.dart'; import 'package:didpay/features/did/did_provider.dart'; import 'package:didpay/features/feature_flags/feature_flags_notifier.dart'; -import 'package:didpay/features/pfis/pfis_notifier.dart'; -import 'package:didpay/features/send/send_details_page.dart'; import 'package:didpay/features/send/send_page.dart'; -import 'package:didpay/shared/next_button.dart'; -import 'package:didpay/shared/number/number_pad.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -18,108 +12,45 @@ void main() async { await TestData.initializeDids(); final did = TestData.aliceDid; - final pfis = TestData.getPfis(); - final accountBalance = TestData.getAccountBalance(); - late MockPfisNotifier mockPfisNotifier; late MockFeatureFlagsNotifier mockFeatureFlagsNotifier; setUp(() { - mockPfisNotifier = MockPfisNotifier(pfis); mockFeatureFlagsNotifier = MockFeatureFlagsNotifier([]); }); group('SendPage', () { - Widget sendPageTestWidget() => WidgetHelpers.testableWidget( + Widget sendDetailsPageTestWidget() => WidgetHelpers.testableWidget( child: const SendPage(), overrides: [ didProvider.overrideWithValue(did), - pfisProvider.overrideWith((ref) => mockPfisNotifier), featureFlagsProvider .overrideWith((ref) => mockFeatureFlagsNotifier), - accountBalanceProvider - .overrideWith(() => MockAccountBalanceNotifier(accountBalance)), ], ); - testWidgets('should show Number Pad', (tester) async { - await tester.pumpWidget(sendPageTestWidget()); - - expect(find.byType(NumberPad), findsOneWidget); - }); - - testWidgets('should show send button', (tester) async { - await tester.pumpWidget(sendPageTestWidget()); - - expect(find.widgetWithText(FilledButton, 'Send'), findsOneWidget); - }); - - testWidgets('should show disabled next button while payin in 0', - (tester) async { - await tester.pumpWidget(sendPageTestWidget()); - - final nextButton = find.widgetWithText(NextButton, 'Send'); + testWidgets('should show QR Code CTA', (tester) async { + await tester.pumpWidget(sendDetailsPageTestWidget()); expect( - tester.widget(nextButton).onPressed, - isNull, + find.widgetWithText( + ListTile, + "Don't know their DAP? Scan their QR code instead", + ), + findsOneWidget, ); }); - testWidgets('should show enabled next button when payin is not 0', - (tester) async { - await tester.pumpWidget(sendPageTestWidget()); - - await tester.tap(find.text('1')); - await tester.pumpAndSettle(); - - final nextButton = find.widgetWithText(FilledButton, 'Send'); + testWidgets('should show input field', (tester) async { + await tester.pumpWidget(sendDetailsPageTestWidget()); - expect( - tester.widget(nextButton).onPressed, - isNotNull, - ); + expect(find.byType(TextField), findsOneWidget); }); - testWidgets('should change send amount after number pad press', - (tester) async { - await tester.pumpWidget(sendPageTestWidget()); - - for (var i = 1; i <= 9; i++) { - await tester.tap(find.text('$i')); - await tester.pumpAndSettle(); - - expect(find.widgetWithText(AutoSizeText, '$i'), findsOneWidget); - - await tester.tap(find.text('<')); - await tester.pumpAndSettle(); - } - - expect(find.widgetWithText(AutoSizeText, '0'), findsOneWidget); - }); - - testWidgets( - 'should pad send amount with a leading zero if send amount < a dollar', - (tester) async { - await tester.pumpWidget(sendPageTestWidget()); - - await tester.tap(find.text('.')); - await tester.pumpAndSettle(); - - expect(find.widgetWithText(AutoSizeText, '0.00'), findsOneWidget); - }); - - testWidgets('should navigate to SendDetailsPage on tap of send button', - (tester) async { - await tester.pumpWidget(sendPageTestWidget()); - - await tester.tap(find.text('8')); - await tester.pumpAndSettle(); - - await tester.tap(find.text('Send')); - await tester.pumpAndSettle(); + testWidgets('should show next button', (tester) async { + await tester.pumpWidget(sendDetailsPageTestWidget()); - expect(find.byType(SendDetailsPage), findsOneWidget); + expect(find.widgetWithText(FilledButton, 'Next'), findsOneWidget); }); }); }