Skip to content

Commit

Permalink
feat: add PaymentQuoteWidget (#242)
Browse files Browse the repository at this point in the history
  • Loading branch information
ethan-tbd authored Jul 20, 2024
1 parent ef8243b commit 9c3f170
Show file tree
Hide file tree
Showing 7 changed files with 214 additions and 276 deletions.
111 changes: 62 additions & 49 deletions lib/features/payment/payment_details_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ import 'package:didpay/features/kcc/kcc_consent_page.dart';
import 'package:didpay/features/payment/payment_details_state.dart';
import 'package:didpay/features/payment/payment_method.dart';
import 'package:didpay/features/payment/payment_methods_page.dart';
import 'package:didpay/features/payment/payment_review_page.dart';
import 'package:didpay/features/payment/payment_quote_widget.dart';
import 'package:didpay/features/payment/payment_state.dart';
import 'package:didpay/features/payment/payment_types_page.dart';
import 'package:didpay/features/pfis/pfi.dart';
import 'package:didpay/features/tbdex/tbdex_quote_notifier.dart';
import 'package:didpay/features/tbdex/tbdex_service.dart';
import 'package:didpay/features/transaction/transaction.dart';
import 'package:didpay/features/vcs/vcs_notifier.dart';
Expand All @@ -33,6 +34,8 @@ class PaymentDetailsPage extends HookConsumerWidget {

@override
Widget build(BuildContext context, WidgetRef ref) {
final quote = useState<AsyncValue<Quote?>>(ref.watch(quoteProvider));

final rfq = useState<AsyncValue<Rfq>?>(null);
final state = useState<PaymentDetailsState>(
paymentState.paymentDetailsState ?? PaymentDetailsState(),
Expand All @@ -56,19 +59,31 @@ class PaymentDetailsPage extends HookConsumerWidget {
[state.value.selectedPaymentType],
);

final isSendingRfq = rfq.value?.isLoading ?? false;
final isAwaiting =
(rfq.value?.isLoading ?? false) || (quote.value.isLoading);

return PopScope(
canPop: !isSendingRfq,
canPop: !isAwaiting,
onPopInvoked: (_) {
if (isSendingRfq) rfq.value = null;
if (isAwaiting) {
rfq.value = null;
quote.value = const AsyncData(null);
}
},
child: Scaffold(
appBar: AppBar(),
body: SafeArea(
child: rfq.value != null
? rfq.value!.when(
data: (_) => Container(),
data: (sentRfq) => PaymentQuoteWidget(
paymentState: paymentState.copyWith(
paymentDetailsState: state.value
.copyWith(exchangeId: sentRfq.metadata.exchangeId),
),
quote: quote,
rfq: rfq,
ref: ref,
),
loading: () => LoadingMessage(
message: Loc.of(context).sendingYourRequest,
),
Expand All @@ -82,43 +97,52 @@ class PaymentDetailsPage extends HookConsumerWidget {
),
),
)
: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Header(
title:
paymentState.transactionType == TransactionType.send
? state.value.moneyAddresses != null
? Loc.of(context).checkTheirPaymentDetails
: Loc.of(context).enterTheirPaymentDetails
: Loc.of(context).enterYourPaymentDetails,
subtitle: Loc.of(context).makeSureInfoIsCorrect,
),
if (state.value.hasMultiplePaymentTypes)
_buildPaymentTypeSelector(
context,
state,
),
if (state.value.hasNoPaymentTypes ||
state.value.selectedPaymentType != null)
_buildPaymentMethodSelector(
context,
availableMethods,
state,
),
_buildPaymentForm(
context,
ref,
state,
rfq,
),
],
),
: _buildPage(context, ref, availableMethods, state, rfq),
),
),
);
}

Widget _buildPage(
BuildContext context,
WidgetRef ref,
List<PaymentMethod>? availableMethods,
ValueNotifier<PaymentDetailsState> state,
ValueNotifier<AsyncValue<Rfq>?> rfq,
) {
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Header(
title: paymentState.transactionType == TransactionType.send
? state.value.moneyAddresses != null
? Loc.of(context).checkTheirPaymentDetails
: Loc.of(context).enterTheirPaymentDetails
: Loc.of(context).enterYourPaymentDetails,
subtitle: Loc.of(context).makeSureInfoIsCorrect,
),
if (state.value.hasMultiplePaymentTypes)
_buildPaymentTypeSelector(
context,
state,
),
if (state.value.hasNoPaymentTypes ||
state.value.selectedPaymentType != null)
_buildPaymentMethodSelector(
context,
availableMethods,
state,
),
_buildPaymentForm(
context,
ref,
state,
rfq,
),
],
);
}

Widget _buildPaymentForm(
BuildContext context,
WidgetRef ref,
Expand Down Expand Up @@ -266,18 +290,7 @@ class PaymentDetailsPage extends HookConsumerWidget {
);

if (context.mounted && rfq.value != null) {
await Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => PaymentReviewPage(
paymentState: updatedPaymentState.copyWith(
paymentDetailsState: updatedPaymentState.paymentDetailsState
?.copyWith(exchangeId: sentRfq.metadata.exchangeId),
),
),
),
);

if (context.mounted) rfq.value = null;
rfq.value = AsyncData(sentRfq);
}
} on Exception catch (e) {
rfq.value = AsyncError(e, StackTrace.current);
Expand Down
1 change: 0 additions & 1 deletion lib/features/payment/payment_order_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import 'package:didpay/features/tbdex/tbdex_service.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/loading_message.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
Expand Down
91 changes: 91 additions & 0 deletions lib/features/payment/payment_quote_widget.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import 'package:didpay/features/payment/payment_review_page.dart';
import 'package:didpay/features/payment/payment_state.dart';
import 'package:didpay/features/tbdex/tbdex_quote_notifier.dart';
import 'package:didpay/l10n/app_localizations.dart';
import 'package:didpay/shared/error_message.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';
import 'package:tbdex/tbdex.dart';

class PaymentQuoteWidget extends HookWidget {
final PaymentState paymentState;
final ValueNotifier<AsyncValue<Quote?>> quote;
final ValueNotifier<AsyncValue<Rfq>?> rfq;
final WidgetRef ref;

const PaymentQuoteWidget({
required this.paymentState,
required this.quote,
required this.rfq,
required this.ref,
super.key,
});

@override
Widget build(BuildContext context) {
TbdexQuoteNotifier getQuoteNotifier() => ref.read(quoteProvider.notifier);

useEffect(
() {
Future.microtask(() async {
if (context.mounted) {
await _pollForQuote(context, ref, getQuoteNotifier());
}
});
return getQuoteNotifier().stopPolling;
},
[],
);

return quote.value.when(
data: (q) => Container(),
loading: () => LoadingMessage(
message: Loc.of(context).gettingYourQuote,
),
error: (error, _) => ErrorMessage(
message: error.toString(),
onRetry: () => _pollForQuote(context, ref, getQuoteNotifier()),
),
);
}

Future<void> _pollForQuote(
BuildContext context,
WidgetRef ref,
TbdexQuoteNotifier quoteNotifier,
) async {
quote.value = const AsyncLoading();

try {
final receivedQuote = await quoteNotifier.startPolling(
paymentState.paymentAmountState?.pfiDid ?? '',
paymentState.paymentDetailsState?.exchangeId ?? '',
);

if (context.mounted && receivedQuote != null) {
quoteNotifier.stopPolling();

await Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => PaymentReviewPage(
paymentState: paymentState.copyWith(
quote: receivedQuote,
),
),
),
);

if (context.mounted) {
quote.value = const AsyncData(null);
rfq.value = null;
}
}
} on Exception catch (e) {
if (context.mounted) {
quote.value = AsyncError(e, StackTrace.current);
}
}
}
}
Loading

0 comments on commit 9c3f170

Please sign in to comment.