diff --git a/lib/routes/home/home_page.dart b/lib/routes/home/home_page.dart index 719cd9295..6c140ad3f 100644 --- a/lib/routes/home/home_page.dart +++ b/lib/routes/home/home_page.dart @@ -1,3 +1,6 @@ +import 'dart:io'; + +import 'package:breez_translations/breez_translations_locales.dart'; import 'package:c_breez/handlers/check_version_handler.dart'; import 'package:c_breez/handlers/connectivity_handler.dart'; import 'package:c_breez/handlers/handler.dart'; @@ -7,11 +10,11 @@ import 'package:c_breez/handlers/payment_result_handler.dart'; import 'package:c_breez/routes/home/account_page.dart'; import 'package:c_breez/routes/home/widgets/app_bar/home_app_bar.dart'; import 'package:c_breez/routes/home/widgets/bottom_actions_bar/bottom_actions_bar.dart'; -import 'package:c_breez/routes/home/widgets/close_popup.dart'; import 'package:c_breez/routes/home/widgets/drawer/home_drawer.dart'; import 'package:c_breez/routes/home/widgets/fade_in_widget.dart'; import 'package:c_breez/routes/home/widgets/qr_action_button.dart'; import 'package:c_breez/routes/security/auto_lock_mixin.dart'; +import 'package:c_breez/widgets/error_dialog.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; @@ -69,11 +72,27 @@ class HomeState extends State with AutoLockMixin, HandlerContextProvider { final themeData = Theme.of(context); final mediaSize = MediaQuery.of(context).size; - return WillPopScope( - onWillPop: willPopCallback( - context, - canCancel: () => _scaffoldKey.currentState?.isDrawerOpen ?? false, - ), + return PopScope( + canPop: false, + onPopInvoked: (_) async { + // Only close drawer if it's open + final NavigatorState navigator = Navigator.of(context); + if (_scaffoldKey.currentState?.isDrawerOpen ?? false) { + navigator.pop(); + return; + } + + // If drawer is not open, prompt user to approve exiting the app + final texts = context.texts(); + final bool? shouldPop = await promptAreYouSure( + context, + texts.close_popup_title, + Text(texts.close_popup_message), + ); + if (shouldPop ?? false) { + exit(0); + } + }, child: SizedBox( height: mediaSize.height, width: mediaSize.width, diff --git a/lib/routes/home/widgets/close_popup.dart b/lib/routes/home/widgets/close_popup.dart deleted file mode 100644 index df75fe4f5..000000000 --- a/lib/routes/home/widgets/close_popup.dart +++ /dev/null @@ -1,28 +0,0 @@ -import 'dart:io'; - -import 'package:breez_translations/breez_translations_locales.dart'; -import 'package:c_breez/widgets/error_dialog.dart'; -import 'package:flutter/widgets.dart'; - -WillPopCallback willPopCallback( - BuildContext context, { - bool immediateExit = false, - String? title, - String? message, - required Function canCancel, -}) { - final texts = context.texts(); - return () async { - if (canCancel()) return true; - return promptAreYouSure( - context, - title ?? texts.close_popup_title, - Text(message ?? texts.close_popup_message), - ).then((ok) { - if (ok == true && immediateExit) { - exit(0); - } - return ok == true; - }); - }; -} diff --git a/lib/routes/home/widgets/drawer/breez_avatar_dialog.dart b/lib/routes/home/widgets/drawer/breez_avatar_dialog.dart index ab41178f8..33e96d909 100644 --- a/lib/routes/home/widgets/drawer/breez_avatar_dialog.dart +++ b/lib/routes/home/widgets/drawer/breez_avatar_dialog.dart @@ -40,8 +40,8 @@ class BreezAvatarDialogState extends State { @override Widget build(BuildContext context) { - return WillPopScope( - onWillPop: () => Future.value(!isUploading), + return PopScope( + canPop: !isUploading, child: StatefulBuilder( builder: (context, setState) { final texts = context.texts(); @@ -139,6 +139,7 @@ class BreezAvatarDialogState extends State { setState(() { isUploading = true; }); + await Future.delayed(const Duration(seconds: 15)); var userName = nameInputController.text.isNotEmpty ? nameInputController.text : userBloc.state.profileSettings.name; diff --git a/lib/routes/initial_walkthrough/mnemonics/enter_mnemonics_page.dart b/lib/routes/initial_walkthrough/mnemonics/enter_mnemonics_page.dart index 0a950eb95..40f44dcf1 100644 --- a/lib/routes/initial_walkthrough/mnemonics/enter_mnemonics_page.dart +++ b/lib/routes/initial_walkthrough/mnemonics/enter_mnemonics_page.dart @@ -1,5 +1,3 @@ -import 'dart:async'; - import 'package:breez_translations/breez_translations_locales.dart'; import 'package:c_breez/routes/initial_walkthrough/mnemonics/widgets/restore_form_page.dart'; import 'package:c_breez/widgets/back_button.dart' as back_button; @@ -26,8 +24,17 @@ class EnterMnemonicsPageState extends State { final texts = context.texts(); final query = MediaQuery.of(context); - return WillPopScope( - onWillPop: _onWillPop, + return PopScope( + canPop: _currentPage == 1, + onPopInvoked: (_) async { + if (_currentPage > 1) { + FocusScope.of(context).requestFocus(FocusNode()); + setState(() { + _currentPage--; + }); + return; + } + }, child: Scaffold( appBar: AppBar( automaticallyImplyLeading: false, @@ -68,17 +75,4 @@ class EnterMnemonicsPageState extends State { ), ); } - - Future _onWillPop() async { - if (_currentPage == 1) { - return true; - } else if (_currentPage > 1) { - FocusScope.of(context).requestFocus(FocusNode()); - setState(() { - _currentPage--; - }); - return false; - } - return false; - } } diff --git a/lib/routes/payment_options/payment_options_page.dart b/lib/routes/payment_options/payment_options_page.dart index 4afae427a..51e16123f 100644 --- a/lib/routes/payment_options/payment_options_page.dart +++ b/lib/routes/payment_options/payment_options_page.dart @@ -19,8 +19,11 @@ class PaymentOptionsPage extends StatelessWidget { final themeData = Theme.of(context); final bloc = context.read(); - return WillPopScope( - onWillPop: () => bloc.cancelEditing().then((_) => true), + return PopScope( + canPop: true, + onPopInvoked: (_) async { + await bloc.cancelEditing(); + }, child: Scaffold( appBar: AppBar( key: GlobalKey(), diff --git a/lib/routes/security/lock_screen.dart b/lib/routes/security/lock_screen.dart index 175566fe0..5ae4a5fc0 100644 --- a/lib/routes/security/lock_screen.dart +++ b/lib/routes/security/lock_screen.dart @@ -6,7 +6,6 @@ import 'package:c_breez/bloc/security/security_state.dart'; import 'package:c_breez/routes/security/widget/pin_code_widget.dart'; import 'package:c_breez/theme/breez_light_theme.dart'; import 'package:c_breez/widgets/route.dart'; -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:hydrated_bloc/hydrated_bloc.dart'; @@ -26,8 +25,8 @@ class LockScreen extends StatelessWidget { final texts = context.texts(); final navigator = Navigator.of(context); - return WillPopScope( - onWillPop: () => SynchronousFuture(false), + return PopScope( + canPop: false, child: Scaffold( body: BlocBuilder( builder: (context, state) { diff --git a/lib/user_app.dart b/lib/user_app.dart index f843ec2b4..64b72fe10 100644 --- a/lib/user_app.dart +++ b/lib/user_app.dart @@ -128,10 +128,8 @@ class UserApp extends StatelessWidget { ); case '/': return FadeInRoute( - builder: (_) => WillPopScope( - onWillPop: () async { - return !await _homeNavigatorKey.currentState!.maybePop(); - }, + builder: (_) => NavigatorPopHandler( + onPop: () => _homeNavigatorKey.currentState!.pop(), child: Navigator( initialRoute: "/", key: _homeNavigatorKey, diff --git a/lib/widgets/error_dialog.dart b/lib/widgets/error_dialog.dart index 64a223ff6..408dc07fe 100644 --- a/lib/widgets/error_dialog.dart +++ b/lib/widgets/error_dialog.dart @@ -17,15 +17,14 @@ Future promptError( final themeData = Theme.of(context); bool canPop = !disableBack; - Future canPopCallback() => Future.value(canPop); return showDialog( useRootNavigator: false, context: context, barrierDismissible: false, // user must tap button! builder: (BuildContext context) { - return WillPopScope( - onWillPop: canPopCallback, + return PopScope( + canPop: canPop, child: AlertDialog( contentPadding: const EdgeInsets.fromLTRB(16.0, 16.0, 16.0, 0.0), title: title == null ? null : Text(title), diff --git a/lib/widgets/payment_dialogs/payment_request_dialog.dart b/lib/widgets/payment_dialogs/payment_request_dialog.dart index 0737f82fd..94fcfeb3a 100644 --- a/lib/widgets/payment_dialogs/payment_request_dialog.dart +++ b/lib/widgets/payment_dialogs/payment_request_dialog.dart @@ -1,5 +1,3 @@ -import 'dart:async'; - import 'package:c_breez/bloc/account/account_bloc.dart'; import 'package:c_breez/models/invoice.dart'; import 'package:c_breez/widgets/payment_dialogs/payment_confirmation_dialog.dart'; @@ -35,62 +33,79 @@ class PaymentRequestDialog extends StatefulWidget { } class PaymentRequestDialogState extends State { + late AccountBloc accountBloc; PaymentRequestState? _state; String? _amountToPayStr; int? _amountToPay; + ModalRoute? _currentRoute; + @override void initState() { super.initState(); + accountBloc = context.read(); _state = PaymentRequestState.PAYMENT_REQUEST; } @override - Widget build(BuildContext context) { - return WillPopScope( - onWillPop: () => _onWillPop(context), - child: showPaymentRequestDialog(context), - ); + void didChangeDependencies() { + super.didChangeDependencies(); + _currentRoute ??= ModalRoute.of(context); } - Future _onWillPop(BuildContext context) async { - if (_state == PaymentRequestState.PROCESSING_PAYMENT) return false; - context.read().cancelPayment(widget.invoice.bolt11); - return true; + @override + Widget build(BuildContext context) { + return PopScope( + canPop: false, + onPopInvoked: (_) async { + if (_state == PaymentRequestState.PROCESSING_PAYMENT) { + return; + } else { + final NavigatorState navigator = Navigator.of(context); + accountBloc.cancelPayment(widget.invoice.bolt11); + if (_currentRoute != null && _currentRoute!.isActive) { + navigator.removeRoute(_currentRoute!); + } + return; + } + }, + child: showPaymentRequestDialog(), + ); } - Widget showPaymentRequestDialog(BuildContext context) { + Widget showPaymentRequestDialog() { const double minHeight = 220; if (_state == PaymentRequestState.PROCESSING_PAYMENT) { return ProcessingPaymentDialog( firstPaymentItemKey: widget.firstPaymentItemKey, minHeight: minHeight, - paymentFunc: () => context - .read() - .sendPayment(widget.invoice.bolt11, widget.invoice.amountMsat == 0 ? _amountToPay! * 1000 : null), - onStateChange: (state) => _onStateChange(context, state), + paymentFunc: () => accountBloc.sendPayment( + widget.invoice.bolt11, + widget.invoice.amountMsat == 0 ? _amountToPay! * 1000 : null, + ), + onStateChange: (state) => _onStateChange(state), ); } else if (_state == PaymentRequestState.WAITING_FOR_CONFIRMATION) { return PaymentConfirmationDialog( widget.invoice.bolt11, _amountToPay!, _amountToPayStr!, - () => _onStateChange(context, PaymentRequestState.USER_CANCELLED), + () => _onStateChange(PaymentRequestState.USER_CANCELLED), (bolt11, amount) => setState(() { _amountToPay = amount; - _onStateChange(context, PaymentRequestState.PROCESSING_PAYMENT); + _onStateChange(PaymentRequestState.PROCESSING_PAYMENT); }), minHeight, ); } else { return PaymentRequestInfoDialog( widget.invoice, - () => _onStateChange(context, PaymentRequestState.USER_CANCELLED), - () => _onStateChange(context, PaymentRequestState.WAITING_FOR_CONFIRMATION), + () => _onStateChange(PaymentRequestState.USER_CANCELLED), + () => _onStateChange(PaymentRequestState.WAITING_FOR_CONFIRMATION), (bolt11, amount) { _amountToPay = amount; - _onStateChange(context, PaymentRequestState.PROCESSING_PAYMENT); + _onStateChange(PaymentRequestState.PROCESSING_PAYMENT); }, (map) => _setAmountToPay(map), minHeight, @@ -98,14 +113,14 @@ class PaymentRequestDialogState extends State { } } - void _onStateChange(BuildContext context, PaymentRequestState state) { + void _onStateChange(PaymentRequestState state) { if (state == PaymentRequestState.PAYMENT_COMPLETED) { Navigator.of(context).pop(); return; } if (state == PaymentRequestState.USER_CANCELLED) { Navigator.of(context).pop(); - context.read().cancelPayment(widget.invoice.bolt11); + accountBloc.cancelPayment(widget.invoice.bolt11); return; } setState(() {