From 88105d4028525fc4bf51a5f9ca0eaf3456df6914 Mon Sep 17 00:00:00 2001 From: Erdem Yerebasmaz Date: Fri, 7 Jul 2023 03:01:49 +0300 Subject: [PATCH] Only use clipboard item for 'Paste Invoice or ID' button Handle input on clicking "Paste Invoice or ID" Pass clipboard item to input bloc if it's a supported input type Revert inputHandler changes Display a loader when clipboard data is being retrieved, parsed & handled Close bottom sheet upon clicking 'Paste Invoice or ID' button --- lib/handlers/input_handler.dart | 22 +-- .../send_options_bottom_sheet.dart | 137 ++++++++++++------ 2 files changed, 109 insertions(+), 50 deletions(-) diff --git a/lib/handlers/input_handler.dart b/lib/handlers/input_handler.dart index e02ead3e1..b8f89c5ee 100644 --- a/lib/handlers/input_handler.dart +++ b/lib/handlers/input_handler.dart @@ -66,15 +66,7 @@ class InputHandler extends Handler { _handlingRequest = true; handleInputData(inputState.inputData) .then((result) { - _log.v("Input state handled: $result"); - if (result is LNURLPageResult && result.protocol != null) { - final context = contextProvider?.getBuildContext(); - if (context != null) { - handleLNURLPageResult(context, result); - } else { - _log.v("Skipping handling of result: $result because context is null"); - } - } + handleResult(result); }) .whenComplete(() => _handlingRequest = false) .onError((error, _) { @@ -92,6 +84,18 @@ class InputHandler extends Handler { }); } + void handleResult(result) { + _log.v("Input state handled: $result"); + if (result is LNURLPageResult && result.protocol != null) { + final context = contextProvider?.getBuildContext(); + if (context != null) { + handleLNURLPageResult(context, result); + } else { + _log.v("Skipping handling of result: $result because context is null"); + } + } + } + Future handleInputData(dynamic parsedInput) async { _log.v("handle input $parsedInput"); final context = contextProvider?.getBuildContext(); diff --git a/lib/routes/home/widgets/bottom_actions_bar/send_options_bottom_sheet.dart b/lib/routes/home/widgets/bottom_actions_bar/send_options_bottom_sheet.dart index 2989429b8..d32e6d2f8 100644 --- a/lib/routes/home/widgets/bottom_actions_bar/send_options_bottom_sheet.dart +++ b/lib/routes/home/widgets/bottom_actions_bar/send_options_bottom_sheet.dart @@ -3,23 +3,32 @@ import 'package:breez_translations/breez_translations_locales.dart'; import 'package:c_breez/bloc/account/account_bloc.dart'; import 'package:c_breez/bloc/account/account_state.dart'; import 'package:c_breez/bloc/input/input_bloc.dart'; -import 'package:c_breez/bloc/input/input_state.dart'; import 'package:c_breez/routes/home/widgets/bottom_actions_bar/bottom_action_item_image.dart'; import 'package:c_breez/routes/home/widgets/bottom_actions_bar/enter_payment_info_dialog.dart'; -import 'package:c_breez/routes/spontaneous_payment/spontaneous_payment_page.dart'; import 'package:c_breez/routes/withdraw_funds/withdraw_funds_address_page.dart'; import 'package:c_breez/theme/theme_provider.dart' as theme; -import 'package:c_breez/widgets/route.dart'; +import 'package:c_breez/widgets/loader.dart'; import 'package:fimber/fimber.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; final _log = FimberLog("SendOptionsBottomSheet"); -class SendOptionsBottomSheet extends StatelessWidget { +class SendOptionsBottomSheet extends StatefulWidget { final GlobalKey firstPaymentItemKey; - const SendOptionsBottomSheet({super.key, required this.firstPaymentItemKey}); + const SendOptionsBottomSheet({ + super.key, + required this.firstPaymentItemKey, + }); + + @override + State createState() => _SendOptionsBottomSheetState(); +} + +class _SendOptionsBottomSheetState extends State { + ModalRoute? _loaderRoute; @override Widget build(BuildContext context) { @@ -29,20 +38,15 @@ class SendOptionsBottomSheet extends StatelessWidget { mainAxisSize: MainAxisSize.min, children: [ const SizedBox(height: 8.0), - BlocBuilder( - builder: (context, snapshot) { - _log.v("Building paste item with snapshot: $snapshot"); - return ListTile( - leading: const BottomActionItemImage( - iconAssetPath: "src/icon/paste.png", - ), - title: Text( - texts.bottom_action_bar_paste_invoice, - style: theme.bottomSheetTextStyle, - ), - onTap: () => _pasteTapped(context, snapshot.inputData, firstPaymentItemKey), - ); - }, + ListTile( + leading: const BottomActionItemImage( + iconAssetPath: "src/icon/paste.png", + ), + title: Text( + texts.bottom_action_bar_paste_invoice, + style: theme.bottomSheetTextStyle, + ), + onTap: () => _pasteFromClipboard(context), ), Divider( height: 0.0, @@ -69,33 +73,71 @@ class SendOptionsBottomSheet extends StatelessWidget { ); } - void _pasteTapped( - BuildContext context, - dynamic inputData, - GlobalKey firstPaymentItemKey, - ) async { - Navigator.of(context).pop(); - if (inputData is InputType_NodeId) { - _log.v("Input data is of type InputType_NodeId, pushing SpontaneousPaymentPage"); - Navigator.of(context).push( - FadeInRoute( - builder: (_) => SpontaneousPaymentPage( - inputData.nodeId, - firstPaymentItemKey, - ), - ), + // TODO: Improve error handling flow to reduce open Enter Payment Info Dialog calls + Future _pasteFromClipboard(BuildContext context) async { + try { + final inputBloc = context.read(); + // Close bottom sheet + Navigator.of(context).pop(); + _setLoading(true); + // Get clipboard data + await Clipboard.getData("text/plain").then( + (clipboardData) async { + final clipboardText = clipboardData?.text; + _log.v("Clipboard text: $clipboardText"); + if (clipboardText != null) { + // Parse clipboard text + await inputBloc.parseInput(input: clipboardText).then( + (inputType) { + // Handle parsed input + if (!(inputType is InputType_Bolt11 || + inputType is InputType_LnUrlPay || + inputType is InputType_LnUrlWithdraw || + inputType is InputType_LnUrlAuth || + inputType is InputType_LnUrlError || + inputType is InputType_NodeId)) { + _showEnterPaymentInfoDialog(context, widget.firstPaymentItemKey); + } else { + inputBloc.addIncomingInput(clipboardText); + } + }, + ); + } else { + _setLoading(false); + // If clipboard data is empty, display EnterPaymentInfoDialog + _showEnterPaymentInfoDialog( + context, + widget.firstPaymentItemKey, + ); + } + }, ); - } else { - _log.v("Input data is $inputData, showing EnterPaymentInfoDialog"); - await showDialog( - useRootNavigator: false, - context: context, - barrierDismissible: false, - builder: (_) => EnterPaymentInfoDialog(paymentItemKey: firstPaymentItemKey), + } catch (e) { + _setLoading(false); + // If there's an error getting the clipboard data, display EnterPaymentInfoDialog + _showEnterPaymentInfoDialog( + context, + widget.firstPaymentItemKey, ); + } finally { + _setLoading(false); } } + Future _showEnterPaymentInfoDialog( + BuildContext context, + GlobalKey> firstPaymentItemKey, + ) async { + await showDialog( + useRootNavigator: false, + context: context, + barrierDismissible: false, + builder: (_) => EnterPaymentInfoDialog( + paymentItemKey: firstPaymentItemKey, + ), + ); + } + void _sendToBTCAddress(BuildContext context, int maxValue) { final navigator = Navigator.of(context); // Close bottom sheet @@ -110,4 +152,17 @@ class SendOptionsBottomSheet extends StatelessWidget { ), ); } + + void _setLoading(bool visible) { + if (visible && _loaderRoute == null) { + _loaderRoute = createLoaderRoute(context); + Navigator.of(context).push(_loaderRoute!); + return; + } + + if (!visible && (_loaderRoute != null && _loaderRoute!.isActive)) { + _loaderRoute!.navigator?.removeRoute(_loaderRoute!); + _loaderRoute = null; + } + } }