diff --git a/lib/bloc/app_bloc_root.dart b/lib/bloc/app_bloc_root.dart index 2dd6bec3d9..36e87b8821 100644 --- a/lib/bloc/app_bloc_root.dart +++ b/lib/bloc/app_bloc_root.dart @@ -32,6 +32,7 @@ import 'package:web_dex/bloc/coins_bloc/coins_bloc.dart'; import 'package:web_dex/bloc/coins_bloc/coins_repo.dart'; import 'package:web_dex/bloc/coins_manager/coins_manager_bloc.dart'; import 'package:web_dex/bloc/dex_repository.dart'; +import 'package:web_dex/bloc/faucet_button/faucet_button_bloc.dart'; import 'package:web_dex/bloc/market_maker_bot/market_maker_bot/market_maker_bot_bloc.dart'; import 'package:web_dex/bloc/market_maker_bot/market_maker_bot/market_maker_bot_repository.dart'; import 'package:web_dex/bloc/market_maker_bot/market_maker_order_list/market_maker_bot_order_list_repository.dart'; @@ -301,6 +302,9 @@ class AppBlocRoot extends StatelessWidget { sdk: komodoDefiSdk, ), ), + BlocProvider( + create: (context) => FaucetBloc(kdfSdk: context.read()), + ) ], child: _MyAppView(), ), diff --git a/lib/bloc/faucet_button/faucet_button_bloc.dart b/lib/bloc/faucet_button/faucet_button_bloc.dart new file mode 100644 index 0000000000..e55fa2c1bd --- /dev/null +++ b/lib/bloc/faucet_button/faucet_button_bloc.dart @@ -0,0 +1,48 @@ +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:komodo_defi_sdk/komodo_defi_sdk.dart'; +import 'package:web_dex/3p_api/faucet/faucet.dart' as api; +import 'package:web_dex/3p_api/faucet/faucet_response.dart'; +import 'package:web_dex/bloc/faucet_button/faucet_button_event.dart'; +import 'package:web_dex/bloc/faucet_button/faucet_button_state.dart'; +import 'package:logging/logging.dart'; + +class FaucetBloc extends Bloc implements StateStreamable { + final KomodoDefiSdk kdfSdk; + final _log = Logger('FaucetBloc'); + + FaucetBloc({required this.kdfSdk}) : super(FaucetInitial()) { + on(_onFaucetRequest); + } + + Future _onFaucetRequest( + FaucetRequested event, + Emitter emit, + ) async { + if (state is FaucetRequestInProgress) { + final currentLoading = state as FaucetRequestInProgress; + if (currentLoading.address == event.address) { + return; + } + } + + emit(FaucetRequestInProgress(address: event.address)); + + try { + final response = await api.callFaucet(event.coinAbbr, event.address); + + if (response.status == FaucetStatus.success) { + emit(FaucetRequestSuccess(FaucetResponse( + status: response.status, + address: event.address, + message: response.message, + coin: event.coinAbbr, + ))); + } else { + emit(FaucetRequestError("Faucet request failed: ${response.message}")); + } + } catch (error, stackTrace) { + _log.shout('Faucet request failed', error, stackTrace); + emit(FaucetRequestError("Network error: ${error.toString()}")); + } + } +} diff --git a/lib/bloc/faucet_button/faucet_button_event.dart b/lib/bloc/faucet_button/faucet_button_event.dart new file mode 100644 index 0000000000..95c7cab454 --- /dev/null +++ b/lib/bloc/faucet_button/faucet_button_event.dart @@ -0,0 +1,21 @@ +import 'package:equatable/equatable.dart'; + +abstract class FaucetEvent extends Equatable { + const FaucetEvent(); + + @override + List get props => []; +} + +class FaucetRequested extends FaucetEvent { + final String coinAbbr; + final String address; + + const FaucetRequested({ + required this.coinAbbr, + required this.address, + }); + + @override + List get props => [coinAbbr, address]; +} diff --git a/lib/bloc/faucet_button/faucet_button_state.dart b/lib/bloc/faucet_button/faucet_button_state.dart new file mode 100644 index 0000000000..6b26720431 --- /dev/null +++ b/lib/bloc/faucet_button/faucet_button_state.dart @@ -0,0 +1,40 @@ +import 'package:equatable/equatable.dart'; +import 'package:web_dex/3p_api/faucet/faucet_response.dart'; + +abstract class FaucetState extends Equatable { + const FaucetState(); + + @override + List get props => []; +} + +class FaucetInitial extends FaucetState { + const FaucetInitial(); +} + +class FaucetRequestInProgress extends FaucetState { + final String address; + + const FaucetRequestInProgress({required this.address}); + + @override + List get props => [address]; +} + +class FaucetRequestSuccess extends FaucetState { + final FaucetResponse response; + + const FaucetRequestSuccess(this.response); + + @override + List get props => [response]; +} + +class FaucetRequestError extends FaucetState { + final String message; + + const FaucetRequestError(this.message); + + @override + List get props => [message]; +} \ No newline at end of file diff --git a/lib/views/wallet/coin_details/coin_details.dart b/lib/views/wallet/coin_details/coin_details.dart index 5059a5de39..666523eeef 100644 --- a/lib/views/wallet/coin_details/coin_details.dart +++ b/lib/views/wallet/coin_details/coin_details.dart @@ -7,17 +7,17 @@ import 'package:web_dex/bloc/transaction_history/transaction_history_event.dart' import 'package:web_dex/model/coin.dart'; import 'package:web_dex/views/wallet/coin_details/coin_details_info/coin_details_info.dart'; import 'package:web_dex/views/wallet/coin_details/coin_page_type.dart'; -import 'package:web_dex/views/wallet/coin_details/faucet/faucet_page.dart'; +import 'package:web_dex/views/wallet/coin_details/receive/receive_details.dart'; import 'package:web_dex/views/wallet/coin_details/rewards/kmd_reward_claim_success.dart'; import 'package:web_dex/views/wallet/coin_details/rewards/kmd_rewards_info.dart'; import 'package:web_dex/views/wallet/coin_details/withdraw_form/withdraw_form.dart'; class CoinDetails extends StatefulWidget { const CoinDetails({ - super.key, + Key? key, required this.coin, required this.onBackButtonPressed, - }); + }) : super(key: key); final Coin coin; final VoidCallback onBackButtonPressed; @@ -73,13 +73,6 @@ class _CoinDetailsState extends State { onBackButtonPressed: _openInfo, ); - case CoinPageType.faucet: - return FaucetPage( - coinAbbr: widget.coin.abbr, - onBackButtonPressed: _openInfo, - coinAddress: widget.coin.defaultAddress, - ); - case CoinPageType.claim: return KmdRewardsInfo( coin: widget.coin, diff --git a/lib/views/wallet/coin_details/coin_details_info/coin_addresses.dart b/lib/views/wallet/coin_details/coin_details_info/coin_addresses.dart index ef5d76b931..a6b2b8f241 100644 --- a/lib/views/wallet/coin_details/coin_details_info/coin_addresses.dart +++ b/lib/views/wallet/coin_details/coin_details_info/coin_addresses.dart @@ -1,7 +1,9 @@ import 'package:app_theme/app_theme.dart'; +import 'package:decimal/decimal.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:komodo_defi_sdk/komodo_defi_sdk.dart'; import 'package:komodo_defi_types/komodo_defi_types.dart'; import 'package:komodo_ui_kit/komodo_ui_kit.dart'; import 'package:qr_flutter/qr_flutter.dart'; @@ -14,6 +16,8 @@ import 'package:web_dex/generated/codegen_loader.g.dart'; import 'package:web_dex/model/coin.dart'; import 'package:web_dex/shared/utils/utils.dart'; import 'package:web_dex/shared/widgets/coin_type_tag.dart'; +import 'package:web_dex/views/wallet/coin_details/coin_page_type.dart'; +import 'package:web_dex/views/wallet/coin_details/faucet/faucet_button.dart'; import 'package:web_dex/views/wallet/common/address_copy_button.dart'; import 'package:web_dex/views/wallet/common/address_icon.dart'; import 'package:web_dex/views/wallet/common/address_text.dart'; @@ -22,16 +26,28 @@ class CoinAddresses extends StatefulWidget { const CoinAddresses({ super.key, required this.coin, + required this.setPageType, }); final Coin coin; + final void Function(CoinPageType) setPageType; @override State createState() => _CoinAddressesState(); } class _CoinAddressesState extends State { - CoinAddressesBloc get _addressesBloc => context.read(); + late final CoinAddressesBloc _addressesBloc; + + @override + void initState() { + super.initState(); + final kdfSdk = RepositoryProvider.of(context); + _addressesBloc = CoinAddressesBloc( + kdfSdk, + widget.coin.abbr, + )..add(const LoadAddressesEvent()); + } @override void dispose() { @@ -74,7 +90,7 @@ class _CoinAddressesState extends State { final index = entry.key; final address = entry.value; if (state.hideZeroBalance && - !address.balance.hasValue) { + address.balance.spendable == Decimal.zero) { return const SizedBox(); } @@ -82,6 +98,7 @@ class _CoinAddressesState extends State { address: address, index: index, coin: widget.coin, + setPageType: widget.setPageType, ); }, ), @@ -178,11 +195,13 @@ class AddressCard extends StatelessWidget { required this.address, required this.index, required this.coin, + required this.setPageType, }); final PubkeyInfo address; final int index; final Coin coin; + final void Function(CoinPageType) setPageType; @override Widget build(BuildContext context) { @@ -206,6 +225,17 @@ class AddressCard extends StatelessWidget { const SizedBox(width: 8), AddressText(address: address.address), const SizedBox(width: 8), + if (coin.hasFaucet) + ConstrainedBox( + constraints: BoxConstraints( + minWidth: 80, + maxWidth: isMobile ? 100 : 160, + ), + child: FaucetButton( + coinAbbr: coin.abbr, + address: address, + ), + ), SwapAddressTag(address: address), const Spacer(), AddressCopyButton(address: address.address), @@ -220,14 +250,28 @@ class AddressCard extends StatelessWidget { const SizedBox(height: 4), ], ) - : Row( - children: [ - AddressText(address: address.address), - const SizedBox(width: 8), - AddressCopyButton(address: address.address), - QrButton(coin: coin, address: address), - SwapAddressTag(address: address), - ], + : SizedBox( + width: double.infinity, + child: Row( + children: [ + AddressText(address: address.address), + const SizedBox(width: 8), + AddressCopyButton(address: address.address), + QrButton(coin: coin, address: address), + if (coin.hasFaucet) + ConstrainedBox( + constraints: BoxConstraints( + minWidth: 80, + maxWidth: isMobile ? 100 : 160, + ), + child: FaucetButton( + coinAbbr: coin.abbr, + address: address, + ), + ), + SwapAddressTag(address: address), + ], + ), ), trailing: isMobile ? null : _Balance(address: address, coin: coin), ), @@ -273,8 +317,60 @@ class QrButton extends StatelessWidget { onPressed: () { showDialog( context: context, - builder: (context) => - PubkeyReceiveDialog(coin: coin, address: address), + builder: (context) => AlertDialog( + title: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + LocaleKeys.receive.tr(), + style: const TextStyle(fontSize: 16), + ), + IconButton( + icon: const Icon(Icons.close), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + ], + ), + content: SizedBox( + width: 450, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + LocaleKeys.onlySendToThisAddress + .tr(args: [abbr2Ticker(coin.abbr)]), + style: const TextStyle(fontSize: 14), + ), + Padding( + padding: const EdgeInsets.all(16.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + LocaleKeys.network.tr(), + style: const TextStyle(fontSize: 14), + ), + CoinTypeTag(coin), + ], + ), + ), + QrCode( + address: address.address, + coinAbbr: coin.abbr, + ), + const SizedBox(height: 16), + Text( + LocaleKeys.scanTheQrCode.tr(), + style: const TextStyle(fontSize: 16), + textAlign: TextAlign.center, + ), + const SizedBox(height: 8), + ], + ), + ), + ), ); }, ); diff --git a/lib/views/wallet/coin_details/coin_details_info/coin_details_common_buttons.dart b/lib/views/wallet/coin_details/coin_details_info/coin_details_common_buttons.dart index a9c084c305..10a42b9ce3 100644 --- a/lib/views/wallet/coin_details/coin_details_info/coin_details_common_buttons.dart +++ b/lib/views/wallet/coin_details/coin_details_info/coin_details_common_buttons.dart @@ -15,7 +15,6 @@ import 'package:web_dex/views/bitrefill/bitrefill_button.dart'; import 'package:web_dex/views/wallet/coin_details/coin_details_info/coin_addresses.dart'; import 'package:web_dex/views/wallet/coin_details/coin_details_info/contract_address_button.dart'; import 'package:web_dex/views/wallet/coin_details/coin_page_type.dart'; -import 'package:web_dex/views/wallet/coin_details/faucet/faucet_button.dart'; class CoinDetailsCommonButtons extends StatelessWidget { const CoinDetailsCommonButtons({ @@ -179,14 +178,6 @@ class CoinDetailsCommonButtonsDesktopLayout extends StatelessWidget { context: context, ), ), - if (coin.hasFaucet) - Container( - margin: const EdgeInsets.only(left: 21), - constraints: const BoxConstraints(maxWidth: 120), - child: FaucetButton( - onPressed: () => selectWidget(CoinPageType.faucet), - ), - ), if (isBitrefillIntegrationEnabled) Container( margin: const EdgeInsets.only(left: 21), diff --git a/lib/views/wallet/coin_details/coin_details_info/coin_details_info.dart b/lib/views/wallet/coin_details/coin_details_info/coin_details_info.dart index bacc478275..cb52765f99 100644 --- a/lib/views/wallet/coin_details/coin_details_info/coin_details_info.dart +++ b/lib/views/wallet/coin_details/coin_details_info/coin_details_info.dart @@ -33,7 +33,6 @@ import 'package:web_dex/views/wallet/coin_details/coin_details_info/coin_address import 'package:web_dex/views/wallet/coin_details/coin_details_info/coin_details_common_buttons.dart'; import 'package:web_dex/views/wallet/coin_details/coin_details_info/coin_details_info_fiat.dart'; import 'package:web_dex/views/wallet/coin_details/coin_page_type.dart'; -import 'package:web_dex/views/wallet/coin_details/faucet/faucet_button.dart'; import 'package:web_dex/views/wallet/coin_details/transactions/transaction_table.dart'; class CoinDetailsInfo extends StatefulWidget { @@ -201,7 +200,8 @@ class _DesktopContent extends StatelessWidget { const SliverToBoxAdapter( child: SizedBox(height: 20), ), - if (selectedTransaction == null) CoinAddresses(coin: coin), + if (selectedTransaction == null) + CoinAddresses(coin: coin, setPageType: setPageType), const SliverToBoxAdapter( child: SizedBox(height: 20), ), @@ -304,7 +304,11 @@ class _MobileContent extends StatelessWidget { const SliverToBoxAdapter( child: SizedBox(height: 20), ), - if (selectedTransaction == null) CoinAddresses(coin: coin), + if (selectedTransaction == null) + CoinAddresses( + coin: coin, + setPageType: setPageType, + ), const SliverToBoxAdapter( child: SizedBox(height: 20), ), @@ -365,10 +369,6 @@ class _CoinDetailsInfoHeader extends StatelessWidget { coin: coin, ), ), - if (coin.hasFaucet) - FaucetButton( - onPressed: () => setPageType(CoinPageType.faucet), - ), _CoinDetailsMarketMetricsTabBar(coin: coin), ], ), @@ -499,51 +499,6 @@ class _CoinDetailsMarketMetricsTabBarState } } -class _FaucetButton extends StatelessWidget { - const _FaucetButton({ - required this.coin, - required this.openFaucet, - }); - - final Coin coin; - final VoidCallback openFaucet; - - @override - Widget build(BuildContext context) { - if (!_isShown) return const SizedBox.shrink(); - - return FocusDecorator( - child: InkWell( - onTap: coin.isSuspended ? null : openFaucet, - child: Opacity( - opacity: coin.isSuspended ? 0.4 : 1, - child: Container( - constraints: const BoxConstraints(maxWidth: 55), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(18), - border: Border.all(color: theme.custom.specificButtonBorderColor), - color: theme.custom.specificButtonBackgroundColor, - ), - padding: const EdgeInsets.symmetric( - vertical: 5, - horizontal: 7, - ), - child: Text( - LocaleKeys.faucet.tr(), - style: const TextStyle( - fontSize: 11, - fontWeight: FontWeight.w500, - ), - ), - ), - ), - ), - ); - } - - bool get _isShown => coin.defaultAddress != null; -} - class _Balance extends StatelessWidget { const _Balance({required this.coin}); final Coin coin; @@ -657,12 +612,6 @@ class _SpecificButton extends StatelessWidget { onTap: () => selectWidget(CoinPageType.claim), ); } - if (coin.hasFaucet) { - return _FaucetButton( - coin: coin, - openFaucet: () => selectWidget(CoinPageType.faucet), - ); - } return const SizedBox.shrink(); } } diff --git a/lib/views/wallet/coin_details/coin_page_type.dart b/lib/views/wallet/coin_details/coin_page_type.dart index 87589a5a48..3dde59b6b5 100644 --- a/lib/views/wallet/coin_details/coin_page_type.dart +++ b/lib/views/wallet/coin_details/coin_page_type.dart @@ -1 +1 @@ -enum CoinPageType { send, faucet, claim, info, claimSuccess } +enum CoinPageType { send, claim, info, claimSuccess } diff --git a/lib/views/wallet/coin_details/faucet/cubit/faucet_cubit.dart b/lib/views/wallet/coin_details/faucet/cubit/faucet_cubit.dart deleted file mode 100644 index 3a01846956..0000000000 --- a/lib/views/wallet/coin_details/faucet/cubit/faucet_cubit.dart +++ /dev/null @@ -1,42 +0,0 @@ -import 'package:bloc/bloc.dart'; -import 'package:komodo_defi_sdk/komodo_defi_sdk.dart'; -import 'package:web_dex/3p_api/faucet/faucet.dart' as api; -import 'package:web_dex/3p_api/faucet/faucet_response.dart'; -import 'package:web_dex/views/wallet/coin_details/faucet/cubit/faucet_state.dart'; - -class FaucetCubit extends Cubit { - final String coinAbbr; - final KomodoDefiSdk kdfSdk; - - FaucetCubit({ - required this.coinAbbr, - required this.kdfSdk, - }) : super(const FaucetInitial()); - - Future callFaucet() async { - emit(const FaucetLoading()); - try { - // Temporary band-aid fix to faucet to support HD wallet - currently - // defaults to calling faucet on all addresses - // TODO: maybe add faucet button per address, or ask user if they want - // to faucet all addresses at once (or offer both options) - final asset = kdfSdk.assets.assetsFromTicker(coinAbbr).single; - final addresses = (await asset.getPubkeys(kdfSdk)).keys; - final faucetFutures = addresses.map((address) async { - return await api.callFaucet(coinAbbr, address.address); - }).toList(); - final responses = await Future.wait(faucetFutures); - if (!responses - .any((response) => response.status == FaucetStatus.success)) { - return emit(FaucetError(responses.first.message)); - } else { - final response = responses.firstWhere( - (response) => response.status == FaucetStatus.success, - ); - return emit(FaucetSuccess(response)); - } - } catch (error) { - return emit(FaucetError(error.toString())); - } - } -} diff --git a/lib/views/wallet/coin_details/faucet/cubit/faucet_state.dart b/lib/views/wallet/coin_details/faucet/cubit/faucet_state.dart deleted file mode 100644 index 60d2fb44ec..0000000000 --- a/lib/views/wallet/coin_details/faucet/cubit/faucet_state.dart +++ /dev/null @@ -1,25 +0,0 @@ -import 'package:web_dex/3p_api/faucet/faucet_response.dart'; - -abstract class FaucetState { - const FaucetState(); -} - -class FaucetInitial extends FaucetState { - const FaucetInitial(); -} - -class FaucetLoading extends FaucetState { - const FaucetLoading(); -} - -class FaucetSuccess extends FaucetState { - final FaucetResponse response; - - const FaucetSuccess(this.response); -} - -class FaucetError extends FaucetState { - final String message; - - const FaucetError(this.message); -} diff --git a/lib/views/wallet/coin_details/faucet/faucet_button.dart b/lib/views/wallet/coin_details/faucet/faucet_button.dart index fd18beef33..0bb72d492d 100644 --- a/lib/views/wallet/coin_details/faucet/faucet_button.dart +++ b/lib/views/wallet/coin_details/faucet/faucet_button.dart @@ -3,32 +3,106 @@ import 'package:flutter/material.dart'; import 'package:web_dex/common/screen.dart'; import 'package:web_dex/generated/codegen_loader.g.dart'; import 'package:web_dex/shared/ui/ui_primary_button.dart'; +import 'package:komodo_defi_types/komodo_defi_types.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:web_dex/bloc/faucet_button/faucet_button_bloc.dart'; +import 'package:web_dex/bloc/faucet_button/faucet_button_event.dart'; +import 'package:web_dex/bloc/faucet_button/faucet_button_state.dart'; +import 'package:web_dex/views/wallet/coin_details/faucet/faucet_view.dart'; -class FaucetButton extends StatelessWidget { +class FaucetButton extends StatefulWidget { const FaucetButton({ - Key? key, - required this.onPressed, + super.key, + required this.coinAbbr, + required this.address, this.enabled = true, - }) : super(key: key); + }); final bool enabled; - final VoidCallback onPressed; + final String coinAbbr; + final PubkeyInfo address; + @override + State createState() => _FaucetButtonState(); +} + +class _FaucetButtonState extends State { @override Widget build(BuildContext context) { final ThemeData themeData = Theme.of(context); - return UiPrimaryButton( - key: const Key('coin-details-faucet-button'), - height: isMobile ? 52 : 40, - textStyle: themeData.textTheme.labelLarge - ?.copyWith(fontSize: 14, fontWeight: FontWeight.w600), - backgroundColor: themeData.colorScheme.tertiary, - text: LocaleKeys.faucet.tr(), - prefix: const Padding( - padding: EdgeInsets.only(right: 10), - child: Icon(Icons.local_drink_rounded, color: Colors.blue), - ), - onPressed: !enabled ? null : onPressed, + + return BlocConsumer( + listenWhen: (previous, current) { + final isLoading = current is FaucetRequestInProgress && current.address == widget.address.address; + final didStopLoading = previous is FaucetRequestInProgress && previous.address == widget.address.address; + + return isLoading || didStopLoading; + }, + listener: (context, state) { + if (state is FaucetRequestInProgress) { + showDialog( + context: context, + barrierDismissible: false, + builder: (dialogContext) => BlocProvider.value( + value: context.read(), + child: FaucetView( + coinAbbr: widget.coinAbbr, + coinAddress: widget.address.address, + onClose: () { + Navigator.of(dialogContext).pop(); + }, + ), + ), + ); + } + }, + builder: (context, state) { + final isLoading = + state is FaucetRequestInProgress && state.address == widget.address.address; + return Padding( + padding: EdgeInsets.only(left: isMobile ? 4 : 8), + child: Container( + decoration: BoxDecoration( + color: isLoading + ? themeData.colorScheme.tertiary.withAlpha(130) + : themeData.colorScheme.tertiary, + borderRadius: BorderRadius.circular(16.0), + ), + child: UiPrimaryButton( + key: Key('coin-details-faucet-button-${widget.address.address}'), + height: isMobile ? 24.0 : 32.0, + backgroundColor: themeData.colorScheme.tertiary, + onPressed: isLoading + ? null + : () { + context.read().add(FaucetRequested( + coinAbbr: widget.coinAbbr, + address: widget.address.address, + )); + }, + child: Padding( + padding: EdgeInsets.symmetric( + vertical: isMobile ? 6.0 : 8.0, + horizontal: isMobile ? 8.0 : 12.0, + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon(Icons.local_drink_rounded, + color: Colors.blue, size: isMobile ? 14 : 16), + Text( + LocaleKeys.faucet.tr(), + style: TextStyle( + fontSize: isMobile ? 9 : 12, + fontWeight: FontWeight.w500), + ), + ], + ), + ), + ), + ), + ); + }, ); } } diff --git a/lib/views/wallet/coin_details/faucet/faucet_page.dart b/lib/views/wallet/coin_details/faucet/faucet_page.dart index 34c9fa9964..918dde3d51 100644 --- a/lib/views/wallet/coin_details/faucet/faucet_page.dart +++ b/lib/views/wallet/coin_details/faucet/faucet_page.dart @@ -1,11 +1,7 @@ import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:komodo_defi_sdk/komodo_defi_sdk.dart'; import 'package:web_dex/views/wallet/coin_details/faucet/faucet_view.dart'; -import 'cubit/faucet_cubit.dart'; - -class FaucetPage extends StatelessWidget { +class FaucetPage extends StatefulWidget { const FaucetPage({ Key? key, required this.coinAbbr, @@ -14,17 +10,20 @@ class FaucetPage extends StatelessWidget { }) : super(key: key); final String coinAbbr; - final String? coinAddress; + final String coinAddress; final VoidCallback onBackButtonPressed; + @override + _FaucetPageState createState() => _FaucetPageState(); +} + +class _FaucetPageState extends State { @override Widget build(BuildContext context) { - final kdfSdk = RepositoryProvider.of(context); - return BlocProvider( - create: (context) => FaucetCubit(coinAbbr: coinAbbr, kdfSdk: kdfSdk), - child: FaucetView( - onBackButtonPressed: onBackButtonPressed, - ), + return FaucetView( + coinAbbr: widget.coinAbbr, + coinAddress: widget.coinAddress, + onClose: widget.onBackButtonPressed, ); } } diff --git a/lib/views/wallet/coin_details/faucet/faucet_view.dart b/lib/views/wallet/coin_details/faucet/faucet_view.dart index c2ea1447c0..2f305f7dd2 100644 --- a/lib/views/wallet/coin_details/faucet/faucet_view.dart +++ b/lib/views/wallet/coin_details/faucet/faucet_view.dart @@ -3,57 +3,57 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:web_dex/3p_api/faucet/faucet_response.dart'; +import 'package:web_dex/bloc/faucet_button/faucet_button_state.dart'; import 'package:web_dex/generated/codegen_loader.g.dart'; -import 'package:web_dex/views/common/page_header/page_header.dart'; -import 'package:web_dex/views/common/pages/page_layout.dart'; -import 'package:web_dex/views/wallet/coin_details/faucet/cubit/faucet_cubit.dart'; -import 'package:web_dex/views/wallet/coin_details/faucet/cubit/faucet_state.dart'; import 'package:web_dex/views/wallet/coin_details/faucet/widgets/faucet_message.dart'; import 'package:komodo_ui_kit/komodo_ui_kit.dart'; +import 'package:web_dex/bloc/faucet_button/faucet_button_bloc.dart'; class FaucetView extends StatelessWidget { - const FaucetView({Key? key, required this.onBackButtonPressed}) - : super(key: key); + const FaucetView({ + Key? key, + required this.coinAbbr, + required this.coinAddress, + required this.onClose, + }) : super(key: key); - final VoidCallback onBackButtonPressed; + final String coinAbbr; + final String coinAddress; + final VoidCallback onClose; @override Widget build(BuildContext context) { - return BlocBuilder( - builder: (context, state) { - if (state is FaucetInitial) { - context.read().callFaucet(); - } - final scrollController = ScrollController(); - return PageLayout( - header: PageHeader( - title: title(state), - backText: LocaleKeys.backToWallet.tr(), - onBackButtonPressed: onBackButtonPressed, - ), - content: Flexible( - child: DexScrollbar( - scrollController: scrollController, - child: SingleChildScrollView( - controller: scrollController, - child: _StatesOfPage( + return Dialog( + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), + insetPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 24), + child: Container( + constraints: const BoxConstraints(maxWidth: 400), + padding: const EdgeInsets.all(16), + child: BlocBuilder( + buildWhen: (previous, current) => previous != current, + builder: (context, state) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + _DialogHeader(title: title(state), onClose: onClose), + _StatesOfPage( state: state, - onBackButtonPressed: onBackButtonPressed, + onClose: onClose, ), - ), - ), - ), - ); - }, + ], + ); + }, + ), + ), ); } String title(FaucetState state) { - if (state is FaucetSuccess) { + if (state is FaucetRequestSuccess) { return state.response.status.title; - } else if (state is FaucetError) { + } else if (state is FaucetRequestError) { return LocaleKeys.faucetFailureTitle.tr(); - } else if (state is FaucetLoading) { + } else if (state is FaucetRequestInProgress) { return LocaleKeys.faucetLoadingTitle.tr(); } else if (state is FaucetInitial) { return LocaleKeys.faucetInitialTitle.tr(); @@ -62,17 +62,50 @@ class FaucetView extends StatelessWidget { } } +class _DialogHeader extends StatelessWidget { + final String title; + final VoidCallback onClose; + + const _DialogHeader({required this.title, required this.onClose}); + + @override + Widget build(BuildContext context) { + return SizedBox( + width: double.infinity, + child: Stack( + alignment: Alignment.center, + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 40), + child: Text( + title, + style: Theme.of(context).textTheme.titleLarge, + ), + ), + Positioned( + right: -8, + child: IconButton( + icon: const Icon(Icons.close), + onPressed: onClose, + ), + ), + ], + ), + ); + } +} + class _StatesOfPage extends StatelessWidget { final FaucetState state; - final VoidCallback onBackButtonPressed; - const _StatesOfPage({required this.state, required this.onBackButtonPressed}); + final VoidCallback onClose; + const _StatesOfPage({required this.state, required this.onClose}); @override Widget build(BuildContext context) { final localState = state; - if (localState is FaucetLoading || localState is FaucetInitial) { + if (localState is FaucetRequestInProgress || localState is FaucetInitial) { return const _Loading(); - } else if (localState is FaucetSuccess) { + } else if (localState is FaucetRequestSuccess) { final bool isDenied = localState.response.status == FaucetStatus.denied; return _FaucetResult( color: isDenied @@ -80,14 +113,14 @@ class _StatesOfPage extends StatelessWidget { : theme.custom.headerFloatBoxColor, icon: isDenied ? Icons.close_rounded : Icons.check_rounded, message: localState.response.message, - onBackButtonPressed: onBackButtonPressed, + onClose: onClose, ); - } else if (localState is FaucetError) { + } else if (localState is FaucetRequestError) { return _FaucetResult( color: theme.custom.decreaseColor, icon: Icons.close_rounded, message: localState.message, - onBackButtonPressed: onBackButtonPressed, + onClose: onClose, ); } @@ -116,14 +149,14 @@ class _FaucetResult extends StatelessWidget { final Color color; final IconData icon; final String message; - final VoidCallback onBackButtonPressed; + final VoidCallback onClose; const _FaucetResult({ Key? key, required this.color, required this.icon, required this.message, - required this.onBackButtonPressed, + required this.onClose, }) : super(key: key); @override @@ -148,7 +181,7 @@ class _FaucetResult extends StatelessWidget { child: UiPrimaryButton( text: LocaleKeys.close.tr(), width: 324, - onPressed: onBackButtonPressed, + onPressed: onClose, ), ), const SizedBox(height: 20),