From 37b57dce7299d0a1aa7bc10e9ba7480611bc8639 Mon Sep 17 00:00:00 2001 From: Egor Komarov Date: Wed, 25 Sep 2024 10:06:56 +0200 Subject: [PATCH] feat(EWM-273): staking (#532) * feat(EWM-273): staking * fix: format --------- Co-authored-by: Egor Komarov --- lib/app/router/routs/wallet/wallet.dart | 12 + .../wallet/staking/bloc/staking_bloc.dart | 35 ++- .../staking/bloc/staking_bloc.freezed.dart | 109 +++---- .../staking/bloc/staking_bloc_state.dart | 4 +- .../staking/view/cancel_unstaking_page.dart | 220 ++++++------- .../wallet/staking/view/staking_page.dart | 59 ++-- .../wallet/staking/view/staking_view.dart | 288 ++++++++++-------- .../staking/widgets/how_it_works_sheet.dart | 87 +++--- .../staking/widgets/staking_in_progress.dart | 6 + .../verify_cancel_unstaking_sheet.dart | 16 +- .../data/wallet_prepare_transfer_asset.dart | 51 +--- .../wallet_prepare_transfer_page_wm.dart | 3 +- .../widgets/wallet_prepare_transfer_view.dart | 4 +- .../detail/details_body.dart | 11 - .../token_transfer_info_wm.dart | 12 +- .../wallet_account_actions.dart | 36 ++- lib/utils/utils.dart | 11 + .../amount_input/amount_input.dart} | 40 +-- .../amount_input/amount_input_asset.dart | 49 +++ .../amount_input_asset_select.dart} | 24 +- .../primary_segment_control.dart | 24 +- .../switcher_segment_controls.dart | 6 +- 22 files changed, 579 insertions(+), 528 deletions(-) rename lib/{feature/wallet/wallet_prepare_transfer/wallet_prepare_transfer_page/widgets/wallet_prepare_transfer_amount_input.dart => widgets/amount_input/amount_input.dart} (85%) create mode 100644 lib/widgets/amount_input/amount_input_asset.dart rename lib/{feature/wallet/wallet_prepare_transfer/wallet_prepare_transfer_page/widgets/wallet_prepare_transfer_asset_select.dart => widgets/amount_input/amount_input_asset_select.dart} (87%) diff --git a/lib/app/router/routs/wallet/wallet.dart b/lib/app/router/routs/wallet/wallet.dart index 0a4b9ff33..b352aebdd 100644 --- a/lib/app/router/routs/wallet/wallet.dart +++ b/lib/app/router/routs/wallet/wallet.dart @@ -71,6 +71,10 @@ const walletCancelUnstakingStakingCurrencyCodeQueryParam = 'walletCancelUnstakingStakingCurrencyCode'; const walletCancelUnstakingAttachedFeeQueryParam = 'walletCancelUnstakingStakingAttachedFee'; +const walletCancelUnstakingTokenPriceQueryParam = + 'walletCancelUnstakingTokenPrice'; +const walletCancelUnstakingEverPriceQueryParam = + 'walletCancelUnstakingEverPrice'; const walletCreatePublicKeyQueryParam = 'walletCreatePublicKey'; const walletCreatePasswordQueryParam = 'walletCreatePassword'; @@ -365,6 +369,14 @@ GoRoute get cancelUnstakingRoute { withdrawHours: int.parse( state.uri.queryParameters[walletCancelUnstakingWithdrawHorsQueryParam]!, ), + tokenPrice: Fixed.tryParse( + state.uri.queryParameters[walletCancelUnstakingTokenPriceQueryParam] ?? + '', + ), + everPrice: Fixed.tryParse( + state.uri.queryParameters[walletCancelUnstakingEverPriceQueryParam] ?? + '', + ), ), routes: [ tonWalletSendRoute, diff --git a/lib/feature/wallet/staking/bloc/staking_bloc.dart b/lib/feature/wallet/staking/bloc/staking_bloc.dart index 6737daab7..cebeaaaba 100644 --- a/lib/feature/wallet/staking/bloc/staking_bloc.dart +++ b/lib/feature/wallet/staking/bloc/staking_bloc.dart @@ -6,6 +6,7 @@ import 'package:app/data/models/models.dart'; import 'package:app/di/di.dart'; import 'package:app/feature/wallet/staking/staking.dart'; import 'package:app/generated/generated.dart'; +import 'package:app/widgets/amount_input/amount_input_asset.dart'; import 'package:bloc/bloc.dart'; import 'package:bloc_event_transformers/bloc_event_transformers.dart'; import 'package:collection/collection.dart'; @@ -25,6 +26,7 @@ final _maxPossibleStakeComission = BigInt.parse('100000000'); // 0.1 EVER enum StakingPageType { stake, unstake, inProgress } +// TODO(komarov): refactor class StakingBloc extends Bloc { StakingBloc({ required this.context, @@ -82,6 +84,9 @@ class StakingBloc extends Bloc { _nativeCurrency, ); + CustomCurrency get everWalletCurrency => _everWalletCurrency; + CustomCurrency get stEverWalletCurrency => _stEverWalletCurrency; + void _registerHandlers() { on<_Init>((_, emit) => _init(emit)); on<_SelectMax>((_, emit) => _selectMax()); @@ -214,7 +219,7 @@ class StakingBloc extends Bloc { /// Get input value as Fixed based on [_currentCurrency] Fixed get _currentValue { return Fixed.fromNum( - num.tryParse(_inputController.text) ?? 0.0, + num.tryParse(_inputController.text.trim().replaceAll(',', '.')) ?? 0.0, scale: _currentCurrency.decimalDigits, ); } @@ -261,6 +266,10 @@ class StakingBloc extends Bloc { BigInt attachedAmount; double exchangeRate; Currency receiveCurrency; + Address rootTokenContract; + CustomCurrency currency; + var title = ''; + var tokenSymbol = ''; var imagePath = ''; switch (_type) { @@ -275,7 +284,12 @@ class StakingBloc extends Bloc { ); exchangeRate = _details.stEverSupply / _details.totalAssets; imagePath = nekotonRepository.currentTransport.nativeTokenIcon; + tokenSymbol = nekotonRepository.currentTransport.nativeTokenTicker; + title = nekotonRepository.currentTransport.nativeTokenTicker; + rootTokenContract = + nekotonRepository.currentTransport.nativeTokenAddress; receiveCurrency = _stEverWallet.moneyBalance.currency; + currency = _everWalletCurrency; case StakingPageType.unstake: attachedAmount = staking.stakeWithdrawAttachedFee; balance = _stEverWallet.moneyBalance; @@ -284,11 +298,17 @@ class StakingBloc extends Bloc { ); exchangeRate = _details.totalAssets / _details.stEverSupply; imagePath = Assets.images.stever.stever.path; + tokenSymbol = _stEverWallet.symbol.name; + title = _stEverWallet.symbol.fullName; + rootTokenContract = _stEverWallet.symbol.rootTokenContract; receiveCurrency = _nativeCurrency; + currency = _stEverWalletCurrency; case StakingPageType.inProgress: attachedAmount = staking.stakeRemovePendingWithdrawAttachedFee; exchangeRate = _details.totalAssets / _details.stEverSupply; receiveCurrency = _stEverWallet.moneyBalance.currency; + rootTokenContract = _stEverWallet.symbol.rootTokenContract; + currency = _stEverWalletCurrency; // fake balance balance = Money.fromBigIntWithCurrency(BigInt.zero, Currency.create('-', 0)); @@ -305,11 +325,9 @@ class StakingBloc extends Bloc { return StakingBlocState.data( type: _type, inputController: _inputController, - currentBalance: balance, enteredPrice: enteredPrice, attachedAmount: attachedAmount, exchangeRate: exchangeRate, - imagePath: imagePath, requests: _requests, receiveBalance: _receive, canSubmitAction: canPress, @@ -317,11 +335,20 @@ class StakingBloc extends Bloc { apy: apy, receiveCurrency: receiveCurrency, accountKey: accountPublicKey, + asset: AmountInputAsset( + rootTokenContract: rootTokenContract, + isNative: _type == StakingPageType.stake, + balance: balance, + logoURI: imagePath, + title: title, + tokenSymbol: tokenSymbol, + currency: currency, + ), ); } void _selectMax() { - var max = _dataState.currentBalance; + var max = _dataState.asset.balance; if (_type == StakingPageType.stake) { max = max - comissionMoney; diff --git a/lib/feature/wallet/staking/bloc/staking_bloc.freezed.dart b/lib/feature/wallet/staking/bloc/staking_bloc.freezed.dart index 949ab5f8e..267b07b30 100644 --- a/lib/feature/wallet/staking/bloc/staking_bloc.freezed.dart +++ b/lib/feature/wallet/staking/bloc/staking_bloc.freezed.dart @@ -1008,12 +1008,11 @@ mixin _$StakingBlocState { BigInt attachedAmount, bool canSubmitAction, TextEditingController inputController, - String imagePath, double exchangeRate, Currency receiveCurrency, PublicKey accountKey, - Money currentBalance, Money enteredPrice, + AmountInputAsset asset, Money? receiveBalance, List? requests, double? apy) @@ -1031,12 +1030,11 @@ mixin _$StakingBlocState { BigInt attachedAmount, bool canSubmitAction, TextEditingController inputController, - String imagePath, double exchangeRate, Currency receiveCurrency, PublicKey accountKey, - Money currentBalance, Money enteredPrice, + AmountInputAsset asset, Money? receiveBalance, List? requests, double? apy)? @@ -1054,12 +1052,11 @@ mixin _$StakingBlocState { BigInt attachedAmount, bool canSubmitAction, TextEditingController inputController, - String imagePath, double exchangeRate, Currency receiveCurrency, PublicKey accountKey, - Money currentBalance, Money enteredPrice, + AmountInputAsset asset, Money? receiveBalance, List? requests, double? apy)? @@ -1165,12 +1162,11 @@ class _$PreparingImpl implements _Preparing { BigInt attachedAmount, bool canSubmitAction, TextEditingController inputController, - String imagePath, double exchangeRate, Currency receiveCurrency, PublicKey accountKey, - Money currentBalance, Money enteredPrice, + AmountInputAsset asset, Money? receiveBalance, List? requests, double? apy) @@ -1191,12 +1187,11 @@ class _$PreparingImpl implements _Preparing { BigInt attachedAmount, bool canSubmitAction, TextEditingController inputController, - String imagePath, double exchangeRate, Currency receiveCurrency, PublicKey accountKey, - Money currentBalance, Money enteredPrice, + AmountInputAsset asset, Money? receiveBalance, List? requests, double? apy)? @@ -1217,12 +1212,11 @@ class _$PreparingImpl implements _Preparing { BigInt attachedAmount, bool canSubmitAction, TextEditingController inputController, - String imagePath, double exchangeRate, Currency receiveCurrency, PublicKey accountKey, - Money currentBalance, Money enteredPrice, + AmountInputAsset asset, Money? receiveBalance, List? requests, double? apy)? @@ -1327,12 +1321,11 @@ class _$InitErrorImpl implements _InitError { BigInt attachedAmount, bool canSubmitAction, TextEditingController inputController, - String imagePath, double exchangeRate, Currency receiveCurrency, PublicKey accountKey, - Money currentBalance, Money enteredPrice, + AmountInputAsset asset, Money? receiveBalance, List? requests, double? apy) @@ -1353,12 +1346,11 @@ class _$InitErrorImpl implements _InitError { BigInt attachedAmount, bool canSubmitAction, TextEditingController inputController, - String imagePath, double exchangeRate, Currency receiveCurrency, PublicKey accountKey, - Money currentBalance, Money enteredPrice, + AmountInputAsset asset, Money? receiveBalance, List? requests, double? apy)? @@ -1379,12 +1371,11 @@ class _$InitErrorImpl implements _InitError { BigInt attachedAmount, bool canSubmitAction, TextEditingController inputController, - String imagePath, double exchangeRate, Currency receiveCurrency, PublicKey accountKey, - Money currentBalance, Money enteredPrice, + AmountInputAsset asset, Money? receiveBalance, List? requests, double? apy)? @@ -1515,12 +1506,11 @@ class _$SubscribeErrorImpl implements _SubscribeError { BigInt attachedAmount, bool canSubmitAction, TextEditingController inputController, - String imagePath, double exchangeRate, Currency receiveCurrency, PublicKey accountKey, - Money currentBalance, Money enteredPrice, + AmountInputAsset asset, Money? receiveBalance, List? requests, double? apy) @@ -1541,12 +1531,11 @@ class _$SubscribeErrorImpl implements _SubscribeError { BigInt attachedAmount, bool canSubmitAction, TextEditingController inputController, - String imagePath, double exchangeRate, Currency receiveCurrency, PublicKey accountKey, - Money currentBalance, Money enteredPrice, + AmountInputAsset asset, Money? receiveBalance, List? requests, double? apy)? @@ -1567,12 +1556,11 @@ class _$SubscribeErrorImpl implements _SubscribeError { BigInt attachedAmount, bool canSubmitAction, TextEditingController inputController, - String imagePath, double exchangeRate, Currency receiveCurrency, PublicKey accountKey, - Money currentBalance, Money enteredPrice, + AmountInputAsset asset, Money? receiveBalance, List? requests, double? apy)? @@ -1647,12 +1635,11 @@ abstract class _$$StakingStateImplCopyWith<$Res> { BigInt attachedAmount, bool canSubmitAction, TextEditingController inputController, - String imagePath, double exchangeRate, Currency receiveCurrency, PublicKey accountKey, - Money currentBalance, Money enteredPrice, + AmountInputAsset asset, Money? receiveBalance, List? requests, double? apy}); @@ -1678,12 +1665,11 @@ class __$$StakingStateImplCopyWithImpl<$Res> Object? attachedAmount = null, Object? canSubmitAction = null, Object? inputController = null, - Object? imagePath = null, Object? exchangeRate = null, Object? receiveCurrency = null, Object? accountKey = null, - Object? currentBalance = null, Object? enteredPrice = null, + Object? asset = null, Object? receiveBalance = freezed, Object? requests = freezed, Object? apy = freezed, @@ -1709,10 +1695,6 @@ class __$$StakingStateImplCopyWithImpl<$Res> ? _value.inputController : inputController // ignore: cast_nullable_to_non_nullable as TextEditingController, - imagePath: null == imagePath - ? _value.imagePath - : imagePath // ignore: cast_nullable_to_non_nullable - as String, exchangeRate: null == exchangeRate ? _value.exchangeRate : exchangeRate // ignore: cast_nullable_to_non_nullable @@ -1725,14 +1707,14 @@ class __$$StakingStateImplCopyWithImpl<$Res> ? _value.accountKey : accountKey // ignore: cast_nullable_to_non_nullable as PublicKey, - currentBalance: null == currentBalance - ? _value.currentBalance - : currentBalance // ignore: cast_nullable_to_non_nullable - as Money, enteredPrice: null == enteredPrice ? _value.enteredPrice : enteredPrice // ignore: cast_nullable_to_non_nullable as Money, + asset: null == asset + ? _value.asset + : asset // ignore: cast_nullable_to_non_nullable + as AmountInputAsset, receiveBalance: freezed == receiveBalance ? _value.receiveBalance : receiveBalance // ignore: cast_nullable_to_non_nullable @@ -1768,12 +1750,11 @@ class _$StakingStateImpl implements _StakingState { required this.attachedAmount, required this.canSubmitAction, required this.inputController, - required this.imagePath, required this.exchangeRate, required this.receiveCurrency, required this.accountKey, - required this.currentBalance, required this.enteredPrice, + required this.asset, this.receiveBalance, final List? requests, this.apy}) @@ -1791,8 +1772,6 @@ class _$StakingStateImpl implements _StakingState { final bool canSubmitAction; @override final TextEditingController inputController; - @override - final String imagePath; // How many [receiveBalance] could be received for [currentBalance]. // if we receive stever, then it should be displayed on right side, on left // otherwise @@ -1802,12 +1781,11 @@ class _$StakingStateImpl implements _StakingState { final Currency receiveCurrency; @override final PublicKey accountKey; -// Balance of current selected token (stake-ever, unstake-stever) - @override - final Money currentBalance; // Price in real curreny of entered tokens @override final Money enteredPrice; + @override + final AmountInputAsset asset; // Balance of token user select after action (stake-stever, unstake-ever) @override final Money? receiveBalance; @@ -1829,7 +1807,7 @@ class _$StakingStateImpl implements _StakingState { @override String toString() { - return 'StakingBlocState.data(type: $type, withdrawTime: $withdrawTime, attachedAmount: $attachedAmount, canSubmitAction: $canSubmitAction, inputController: $inputController, imagePath: $imagePath, exchangeRate: $exchangeRate, receiveCurrency: $receiveCurrency, accountKey: $accountKey, currentBalance: $currentBalance, enteredPrice: $enteredPrice, receiveBalance: $receiveBalance, requests: $requests, apy: $apy)'; + return 'StakingBlocState.data(type: $type, withdrawTime: $withdrawTime, attachedAmount: $attachedAmount, canSubmitAction: $canSubmitAction, inputController: $inputController, exchangeRate: $exchangeRate, receiveCurrency: $receiveCurrency, accountKey: $accountKey, enteredPrice: $enteredPrice, asset: $asset, receiveBalance: $receiveBalance, requests: $requests, apy: $apy)'; } @override @@ -1846,18 +1824,15 @@ class _$StakingStateImpl implements _StakingState { other.canSubmitAction == canSubmitAction) && (identical(other.inputController, inputController) || other.inputController == inputController) && - (identical(other.imagePath, imagePath) || - other.imagePath == imagePath) && (identical(other.exchangeRate, exchangeRate) || other.exchangeRate == exchangeRate) && (identical(other.receiveCurrency, receiveCurrency) || other.receiveCurrency == receiveCurrency) && (identical(other.accountKey, accountKey) || other.accountKey == accountKey) && - (identical(other.currentBalance, currentBalance) || - other.currentBalance == currentBalance) && (identical(other.enteredPrice, enteredPrice) || other.enteredPrice == enteredPrice) && + (identical(other.asset, asset) || other.asset == asset) && (identical(other.receiveBalance, receiveBalance) || other.receiveBalance == receiveBalance) && const DeepCollectionEquality().equals(other._requests, _requests) && @@ -1872,12 +1847,11 @@ class _$StakingStateImpl implements _StakingState { attachedAmount, canSubmitAction, inputController, - imagePath, exchangeRate, receiveCurrency, accountKey, - currentBalance, enteredPrice, + asset, receiveBalance, const DeepCollectionEquality().hash(_requests), apy); @@ -1902,12 +1876,11 @@ class _$StakingStateImpl implements _StakingState { BigInt attachedAmount, bool canSubmitAction, TextEditingController inputController, - String imagePath, double exchangeRate, Currency receiveCurrency, PublicKey accountKey, - Money currentBalance, Money enteredPrice, + AmountInputAsset asset, Money? receiveBalance, List? requests, double? apy) @@ -1919,12 +1892,11 @@ class _$StakingStateImpl implements _StakingState { attachedAmount, canSubmitAction, inputController, - imagePath, exchangeRate, receiveCurrency, accountKey, - currentBalance, enteredPrice, + asset, receiveBalance, requests, apy); @@ -1942,12 +1914,11 @@ class _$StakingStateImpl implements _StakingState { BigInt attachedAmount, bool canSubmitAction, TextEditingController inputController, - String imagePath, double exchangeRate, Currency receiveCurrency, PublicKey accountKey, - Money currentBalance, Money enteredPrice, + AmountInputAsset asset, Money? receiveBalance, List? requests, double? apy)? @@ -1959,12 +1930,11 @@ class _$StakingStateImpl implements _StakingState { attachedAmount, canSubmitAction, inputController, - imagePath, exchangeRate, receiveCurrency, accountKey, - currentBalance, enteredPrice, + asset, receiveBalance, requests, apy); @@ -1982,12 +1952,11 @@ class _$StakingStateImpl implements _StakingState { BigInt attachedAmount, bool canSubmitAction, TextEditingController inputController, - String imagePath, double exchangeRate, Currency receiveCurrency, PublicKey accountKey, - Money currentBalance, Money enteredPrice, + AmountInputAsset asset, Money? receiveBalance, List? requests, double? apy)? @@ -2001,12 +1970,11 @@ class _$StakingStateImpl implements _StakingState { attachedAmount, canSubmitAction, inputController, - imagePath, exchangeRate, receiveCurrency, accountKey, - currentBalance, enteredPrice, + asset, receiveBalance, requests, apy); @@ -2059,12 +2027,11 @@ abstract class _StakingState implements StakingBlocState { required final BigInt attachedAmount, required final bool canSubmitAction, required final TextEditingController inputController, - required final String imagePath, required final double exchangeRate, required final Currency receiveCurrency, required final PublicKey accountKey, - required final Money currentBalance, required final Money enteredPrice, + required final AmountInputAsset asset, final Money? receiveBalance, final List? requests, final double? apy}) = _$StakingStateImpl; @@ -2074,18 +2041,16 @@ abstract class _StakingState implements StakingBlocState { int get withdrawTime; // Amount in EVER that will be attached to action BigInt get attachedAmount; bool get canSubmitAction; - TextEditingController get inputController; - String - get imagePath; // How many [receiveBalance] could be received for [currentBalance]. + TextEditingController + get inputController; // How many [receiveBalance] could be received for [currentBalance]. // if we receive stever, then it should be displayed on right side, on left // otherwise double get exchangeRate; Currency get receiveCurrency; - PublicKey - get accountKey; // Balance of current selected token (stake-ever, unstake-stever) - Money get currentBalance; // Price in real curreny of entered tokens - Money - get enteredPrice; // Balance of token user select after action (stake-stever, unstake-ever) + PublicKey get accountKey; // Price in real curreny of entered tokens + Money get enteredPrice; + AmountInputAsset + get asset; // Balance of token user select after action (stake-stever, unstake-ever) Money? get receiveBalance; // Pending withdraw requests List? get requests; // Average profit double? get apy; diff --git a/lib/feature/wallet/staking/bloc/staking_bloc_state.dart b/lib/feature/wallet/staking/bloc/staking_bloc_state.dart index 870380e4d..2e4a0df76 100644 --- a/lib/feature/wallet/staking/bloc/staking_bloc_state.dart +++ b/lib/feature/wallet/staking/bloc/staking_bloc_state.dart @@ -17,17 +17,15 @@ class StakingBlocState with _$StakingBlocState { required BigInt attachedAmount, required bool canSubmitAction, required TextEditingController inputController, - required String imagePath, // How many [receiveBalance] could be received for [currentBalance]. // if we receive stever, then it should be displayed on right side, on left // otherwise required double exchangeRate, required Currency receiveCurrency, required PublicKey accountKey, - // Balance of current selected token (stake-ever, unstake-stever) - required Money currentBalance, // Price in real curreny of entered tokens required Money enteredPrice, + required AmountInputAsset asset, // Balance of token user select after action (stake-stever, unstake-ever) Money? receiveBalance, // Pending withdraw requests diff --git a/lib/feature/wallet/staking/view/cancel_unstaking_page.dart b/lib/feature/wallet/staking/view/cancel_unstaking_page.dart index 3c19d6440..c4d60afbd 100644 --- a/lib/feature/wallet/staking/view/cancel_unstaking_page.dart +++ b/lib/feature/wallet/staking/view/cancel_unstaking_page.dart @@ -3,6 +3,7 @@ import 'package:app/app/service/service.dart'; import 'package:app/data/models/models.dart'; import 'package:app/di/di.dart'; import 'package:app/feature/wallet/wallet.dart'; +import 'package:app/feature/wallet/widgets/account_transactions_tab/detail/details.dart'; import 'package:app/feature/wallet/widgets/account_transactions_tab/widgets/ton_wallet_transaction_status_body.dart'; import 'package:app/generated/generated.dart'; import 'package:app/utils/utils.dart'; @@ -10,7 +11,9 @@ import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:nekoton_repository/nekoton_repository.dart'; import 'package:ui_components_lib/ui_components_lib.dart'; +import 'package:ui_components_lib/v2/ui_components_lib_v2.dart'; +// TODO(komarov): refactor /// Screen that allows user to cancel pending withdraw request class CancelUnstakingPage extends StatelessWidget { const CancelUnstakingPage({ @@ -20,6 +23,8 @@ class CancelUnstakingPage extends StatelessWidget { required this.withdrawHours, required this.stakeCurrency, required this.attachedFee, + required this.tokenPrice, + required this.everPrice, super.key, }); @@ -30,10 +35,12 @@ class CancelUnstakingPage extends StatelessWidget { final int withdrawHours; final Currency stakeCurrency; final BigInt attachedFee; + final Fixed? tokenPrice; + final Fixed? everPrice; @override Widget build(BuildContext context) { - final colors = context.themeStyle.colors; + final theme = context.themeStyleV2; final steverMoney = Money.fromBigIntWithCurrency( request.data.amount, stakeCurrency, @@ -45,108 +52,95 @@ class CancelUnstakingPage extends StatelessWidget { ); return Scaffold( - appBar: const DefaultAppBar(), + appBar: DefaultAppBar( + titleText: LocaleKeys.transactionInformation.tr(), + ), body: Stack( children: [ Positioned.fill( - bottom: commonButtonHeight + DimensSize.d16, + bottom: DimensSizeV2.d90, child: SingleChildScrollView( - child: SeparatedColumn( - crossAxisAlignment: CrossAxisAlignment.start, - separatorSize: DimensSize.d16, - mainAxisSize: MainAxisSize.min, - children: [ - Padding( - padding: const EdgeInsets.symmetric( - horizontal: DimensSize.d16, - ), - child: Text( - LocaleKeys.transactionInformation.tr(), - style: StyleRes.h1.copyWith( - color: context.themeStyle.colors.textPrimary, + padding: const EdgeInsets.symmetric( + horizontal: DimensSizeV2.d16, + vertical: DimensSizeV2.d8, + ), + child: PrimaryCard( + padding: const EdgeInsets.symmetric( + horizontal: DimensSize.d16, + vertical: DimensSize.d8, + ), + borderRadius: BorderRadius.circular(DimensRadiusV2.radius12), + child: SeparatedColumn( + separatorSize: DimensSizeV2.d16, + mainAxisSize: MainAxisSize.min, + children: [ + const SizedBox.shrink(), + _statusDateRow(context), + Text( + LocaleKeys.withdrawHoursNote.tr( + args: [withdrawHours.toString()], + ), + style: StyleRes.secondaryRegular.copyWith( + color: theme.colors.content3, ), ), - ), - ShapedContainerColumn( - mainAxisSize: MainAxisSize.min, - padding: const EdgeInsets.symmetric( - vertical: DimensSize.d16, + const Divider(), + WalletTransactionDetailsItem( + title: LocaleKeys.typeWord.tr(), + value: LocaleKeys.liquidStaking.tr(), ), - separator: const Padding( - padding: EdgeInsets.symmetric(vertical: DimensSize.d8), - child: CommonDivider(), + WalletTransactionDetailsItem( + title: LocaleKeys.unstakeAmount.tr(), + valueWidget: AmountWidget.fromMoney(amount: steverMoney), + tonIconPath: Assets.images.stever.stever.path, + convertedValueWidget: tokenPrice != null + ? AmountWidget.fromMoney( + amount: steverMoney.exchangeToUSD(tokenPrice!), + style: theme.textStyles.labelXSmall.copyWith( + color: theme.colors.content3, + ), + sign: '~ ', + useDefaultFormat: true, + ) + : null, ), - children: [ - _statusDateRow(), - Padding( - padding: const EdgeInsets.symmetric( - horizontal: DimensSize.d16, - ), - child: Text( - LocaleKeys.withdrawHoursNote.tr( - args: [withdrawHours.toString()], - ), - style: StyleRes.secondaryRegular.copyWith( - color: colors.textSecondary, - ), - ), - ), - CommonListTile( - invertTitleSubtitleStyles: true, - padding: const EdgeInsets.symmetric( - horizontal: DimensSize.d16, - ), - titleText: LocaleKeys.typeWord.tr(), - subtitleText: LocaleKeys.liquidStaking.tr(), - ), - CommonListTile( - invertTitleSubtitleStyles: true, - padding: const EdgeInsets.symmetric( - horizontal: DimensSize.d16, - ), - titleText: LocaleKeys.unstakeAmount.tr(), - subtitleChild: MoneyWidget( - money: steverMoney, - style: MoneyWidgetStyle.primary, - ), - ), - CommonListTile( - invertTitleSubtitleStyles: true, - padding: const EdgeInsets.symmetric( - horizontal: DimensSize.d16, - ), - titleText: LocaleKeys.exchangeRate.tr(), - subtitleText: - // ignore: lines_longer_than_80_chars, no-magic-number, binary-expression-operand-order - '1 ${inject().currentTransport.nativeTokenTicker} ≈ ${(1 * exchangeRate).toStringAsFixed(4)} ${stakeCurrency.isoCode}', - ), - CommonListTile( - invertTitleSubtitleStyles: true, - padding: const EdgeInsets.symmetric( - horizontal: DimensSize.d16, - ), - titleText: LocaleKeys.receiveWord.tr(), - subtitleChild: MoneyWidget( - money: everMoney, - style: MoneyWidgetStyle.primary, - signValue: '~', - ), - ), - ], - ), - ], + WalletTransactionDetailsItem( + title: LocaleKeys.exchangeRate.tr(), + value: + // ignore: lines_longer_than_80_chars, no-magic-number, binary-expression-operand-order + '1 ${inject().currentTransport.nativeTokenTicker} ≈ ${(1 * exchangeRate).toStringAsFixed(4)} ${stakeCurrency.isoCode}', + ), + WalletTransactionDetailsItem( + title: LocaleKeys.receiveWord.tr(), + valueWidget: AmountWidget.fromMoney(amount: everMoney), + tonIconPath: Assets.images.stever.stever.path, + convertedValueWidget: everPrice != null + ? AmountWidget.fromMoney( + amount: everMoney.exchangeToUSD(everPrice!), + style: theme.textStyles.labelXSmall.copyWith( + color: theme.colors.content3, + ), + sign: '~ ', + useDefaultFormat: true, + ) + : null, + ), + ], + ), ), ), ), Positioned( - bottom: DimensSize.d16, - left: DimensSize.d16, - right: DimensSize.d16, - child: CommonButton( - buttonType: EverButtonType.secondary, - contentColor: colors.alert, - text: LocaleKeys.cancelUnstaking.tr(), - onPressed: () => tryCancelUnstaking(context), + bottom: DimensSizeV2.d0, + left: DimensSizeV2.d0, + right: DimensSizeV2.d0, + child: Padding( + padding: const EdgeInsets.all(DimensSizeV2.d16), + child: DestructiveButton( + buttonShape: ButtonShape.pill, + title: LocaleKeys.cancelUnstaking.tr(), + onPressed: () => tryCancelUnstaking(context), + ), ), ), ], @@ -154,36 +148,24 @@ class CancelUnstakingPage extends StatelessWidget { ); } - Widget _statusDateRow() { - return Builder( - builder: (context) { - final date = request.data.timestamp; - final colors = context.themeStyle.colors; - final formatter = date.year == NtpTime.now().year - ? DateFormat('MM.dd, HH:mm', context.locale.languageCode) - : DateFormat('MM.dd.y, HH:mm', context.locale.languageCode); + Widget _statusDateRow(BuildContext context) { + final date = request.data.timestamp; + final theme = context.themeStyleV2; + final formatter = date.year == NtpTime.now().year + ? DateFormat('MM.dd, HH:mm', context.locale.languageCode) + : DateFormat('MM.dd.y, HH:mm', context.locale.languageCode); - return Padding( - padding: const EdgeInsets.symmetric( - horizontal: DimensSize.d16, - vertical: DimensSize.d8, + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + formatter.format(date), + style: theme.textStyles.labelXSmall.copyWith( + color: theme.colors.content3, ), - child: SeparatedRow( - children: [ - TonWalletTransactionStatus.unstakingInProgress.chipByStatus, - Expanded( - child: Text( - formatter.format(date), - style: StyleRes.addRegular.copyWith( - color: colors.textSecondary, - ), - textAlign: TextAlign.right, - ), - ), - ], - ), - ); - }, + ), + TonWalletTransactionStatus.unstakingInProgress.chipByStatus, + ], ); } diff --git a/lib/feature/wallet/staking/view/staking_page.dart b/lib/feature/wallet/staking/view/staking_page.dart index 33db04439..7bddbdd0a 100644 --- a/lib/feature/wallet/staking/view/staking_page.dart +++ b/lib/feature/wallet/staking/view/staking_page.dart @@ -7,6 +7,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:nekoton_repository/nekoton_repository.dart'; import 'package:ui_components_lib/ui_components_lib.dart'; +import 'package:ui_components_lib/v2/widgets/widgets.dart'; /// Page that allows user to stake his native token. class StakingPage extends StatelessWidget { @@ -19,6 +20,8 @@ class StakingPage extends StatelessWidget { @override Widget build(BuildContext context) { + final theme = context.themeStyleV2; + return GestureDetector( onTap: () => FocusScope.of(context).unfocus(), child: Scaffold( @@ -43,14 +46,18 @@ class StakingPage extends StatelessWidget { child: Stack( children: [ Positioned.fill( - bottom: commonButtonHeight + DimensSize.d16, + bottom: DimensSizeV2.d90, child: _stakingViewBuilder(), ), Positioned( - bottom: DimensSize.d16, - right: DimensSize.d16, - left: DimensSize.d16, - child: _buttonBuilder(), + bottom: DimensSizeV2.d0, + right: DimensSizeV2.d0, + left: DimensSizeV2.d0, + child: Container( + padding: const EdgeInsets.all(DimensSizeV2.d16), + color: theme.colors.background0, + child: _buttonBuilder(), + ), ), ], ), @@ -64,45 +71,51 @@ class StakingPage extends StatelessWidget { Widget _stakingViewBuilder() { return Builder( builder: (context) { - final colors = context.themeStyle.colors; + final theme = context.themeStyleV2; return SingleChildScrollView( - padding: const EdgeInsets.all(DimensSize.d16), - child: SeparatedColumn( - separatorSize: DimensSize.d16, + padding: const EdgeInsets.symmetric( + horizontal: DimensSizeV2.d16, + vertical: DimensSizeV2.d8, + ), + child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( LocaleKeys.simpleLiquidStaking.tr(), - style: StyleRes.h1.copyWith(color: colors.textPrimary), + style: theme.textStyles.headingXLarge, ), + const SizedBox(height: DimensSizeV2.d12), Text.rich( TextSpan( children: [ TextSpan( text: LocaleKeys.stakeEverReceiverStever.tr(), - style: StyleRes.primaryRegular - .copyWith(color: colors.textPrimary), + style: theme.textStyles.paragraphMedium.copyWith( + color: theme.colors.content3, + ), ), const WidgetSpan( - child: SizedBox(width: DimensSize.d4), + child: SizedBox(width: DimensSizeV2.d4), ), TextSpan( text: LocaleKeys.howItWorks.tr(), - style: - StyleRes.primaryRegular.copyWith(color: colors.blue), + style: theme.textStyles.paragraphMedium.copyWith( + color: theme.colors.primaryA, + ), recognizer: TapGestureRecognizer() ..onTap = () => showStEverHowItWorksSheet(context), ), ], ), ), + const SizedBox(height: DimensSizeV2.d24), BlocBuilder( builder: (context, state) { return state.when( preparing: () => const Center( child: Padding( - padding: EdgeInsets.all(DimensSize.d16), + padding: EdgeInsets.all(DimensSizeV2.d16), child: CommonCircularProgressIndicator( size: CircularIndicatorSize.large, ), @@ -113,11 +126,12 @@ class StakingPage extends StatelessWidget { ), initError: () => Center( child: Padding( - padding: const EdgeInsets.all(DimensSize.d16), + padding: const EdgeInsets.all(DimensSizeV2.d16), child: Text( LocaleKeys.stakingInitError.tr(), - style: StyleRes.primaryRegular - .copyWith(color: colors.textPrimary), + style: theme.textStyles.paragraphMedium.copyWith( + color: theme.colors.negative, + ), textAlign: TextAlign.center, ), ), @@ -216,7 +230,6 @@ class StakingPage extends StatelessWidget { _________, __________, ___________, - ____________, ) => switch (type) { StakingPageType.stake => ( @@ -234,9 +247,9 @@ class StakingPage extends StatelessWidget { if (text == null) return const SizedBox.shrink(); - return CommonButton.primary( - text: text, - fillWidth: true, + return AccentButton( + buttonShape: ButtonShape.pill, + title: text, isLoading: isLoading, onPressed: canPress ? () => context diff --git a/lib/feature/wallet/staking/view/staking_view.dart b/lib/feature/wallet/staking/view/staking_view.dart index e025bf5f6..a03af4874 100644 --- a/lib/feature/wallet/staking/view/staking_view.dart +++ b/lib/feature/wallet/staking/view/staking_view.dart @@ -2,10 +2,13 @@ import 'package:app/data/models/models.dart'; import 'package:app/di/di.dart'; import 'package:app/feature/wallet/wallet.dart'; import 'package:app/generated/generated.dart'; +import 'package:app/widgets/amount_input/amount_input.dart'; +import 'package:app/widgets/amount_input/amount_input_asset.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:nekoton_repository/nekoton_repository.dart'; import 'package:ui_components_lib/ui_components_lib.dart'; +import 'package:ui_components_lib/v2/ui_components_lib_v2.dart'; extension on StakingPageType { String get title { @@ -26,12 +29,11 @@ class StakingView extends StatelessWidget { // ignore: avoid_positional_boolean_parameters this.canSubmitAction, this.inputController, - this.imagePath, this.exchangeRate, this.receiveCurrency, this.accountPublicKey, - this.currentBalance, this.enteredPrice, + this.asset, this.receiveBalance, this.requests, this.apy, { @@ -43,9 +45,6 @@ class StakingView extends StatelessWidget { /// Amount in EVER that will be attached to action final BigInt attachedAmount; - - final String imagePath; - final int withdrawTime; final bool canSubmitAction; final TextEditingController inputController; @@ -54,9 +53,6 @@ class StakingView extends StatelessWidget { /// How many stevers could be received for evers final double? exchangeRate; - /// Balance of current selected token (stake-ever, unstake-stever) - final Money currentBalance; - /// Price in real curreny of entered tokens final Money enteredPrice; @@ -71,34 +67,69 @@ class StakingView extends StatelessWidget { /// Average profit final double? apy; + final AmountInputAsset asset; + @override Widget build(BuildContext context) { + final theme = context.themeStyleV2; + final bloc = context.read(); + return SeparatedColumn( - separatorSize: DimensSize.d16, + separatorSize: DimensSizeV2.d16, crossAxisAlignment: CrossAxisAlignment.start, children: [ SingleChildScrollView( scrollDirection: Axis.horizontal, - child: CommonTabBar( - selectedValue: type, - onChanged: (type) => context - .read() - .add(StakingBlocEvent.changeTab(type)), + child: SwitcherSegmentControls( + fullWidth: false, + currentValue: type, values: [ - StakingPageType.stake, - StakingPageType.unstake, + PrimarySegmentControl( + title: StakingPageType.stake.title, + value: StakingPageType.stake, + size: SegmentControlSize.xsmall, + state: SegmentControlState.normal, + ), + PrimarySegmentControl( + title: StakingPageType.unstake.title, + value: StakingPageType.unstake, + size: SegmentControlSize.xsmall, + state: SegmentControlState.normal, + ), if (requests != null && requests!.isNotEmpty) - StakingPageType.inProgress, + PrimarySegmentControl( + titleSpan: TextSpan( + children: [ + TextSpan(text: StakingPageType.inProgress.title), + const WidgetSpan( + child: SizedBox(width: DimensSizeV2.d8), + ), + TextSpan( + text: requests?.length.toString() ?? '0', + style: TextStyle( + color: Colors.white, + background: Paint() + ..strokeWidth = DimensSizeV2.d8 + ..color = theme.colors.backgroundAccent + ..style = PaintingStyle.stroke + ..strokeJoin = StrokeJoin.round, + ), + ), + ], + ), + value: StakingPageType.inProgress, + size: SegmentControlSize.xsmall, + state: SegmentControlState.normal, + ), ], - builder: (_, e) => e.title, - trailingBuilder: (_, e) => e == StakingPageType.inProgress - ? _pendingWithdrawIndicator() - : null, + onTabChanged: (value) => context + .read() + .add(StakingBlocEvent.changeTab(value)), ), ), switch (type) { - StakingPageType.stake => _stakeUnstakeBody(), - StakingPageType.unstake => _stakeUnstakeBody(), + StakingPageType.stake => _stakeUnstakeBody(context), + StakingPageType.unstake => _stakeUnstakeBody(context), StakingPageType.inProgress => StakingInProgress( requests: requests ?? [], accountKey: accountPublicKey, @@ -106,128 +137,141 @@ class StakingView extends StatelessWidget { stakeCurrency: receiveCurrency, attachedFee: attachedAmount, withdrawHours: withdrawTime, + everPrice: bloc.everWalletCurrency.price, + tokenPrice: bloc.stEverWalletCurrency.price, ), }, ], ); } - Widget _pendingWithdrawIndicator() { - return Builder( - builder: (context) { - final textColor = EverButtonStyleProvider.of(context).contentColor; - final colors = context.themeStyle.colors; - - return Container( - width: DimensSize.d20, - height: DimensSize.d20, - alignment: Alignment.center, - decoration: BoxDecoration( - color: colors.blue, - shape: BoxShape.circle, - ), - child: Text( - requests?.length.toString() ?? '0', - style: StyleRes.addBold.copyWith(color: textColor), - ), - ); - }, - ); - } - - Widget _stakeUnstakeBody() { + Widget _stakeUnstakeBody(BuildContext context) { return SeparatedColumn( crossAxisAlignment: CrossAxisAlignment.start, - separatorSize: DimensSize.d4, + separatorSize: DimensSizeV2.d4, children: [ - _inputField(), - MoneyWidget( - money: currentBalance, - style: MoneyWidgetStyle.secondary, + AmountInput( + controller: inputController, + selectedAsset: asset, + onMaxAmount: () => context + .read() + .add(const StakingBlocEvent.selectMax()), + onSubmitted: (value) => inputController.text = value, + ), + const SizedBox(height: DimensSizeV2.d12), + _InfoField( + currentCurrency: asset.balance.currency, + receiveCurrency: receiveCurrency, + exchangeRate: exchangeRate ?? 1, + attachedAmount: Money.fromBigIntWithCurrency( + attachedAmount, + Currencies()[inject() + .currentTransport + .nativeTokenTicker]!, + ), + receiveBalance: receiveBalance, + apy: apy, ), - const SizedBox(height: DimensSize.d12), - _infoField(), ], ); } +} - Widget _inputField() { - return Builder( - builder: (context) { - final colors = context.themeStyle.colors; +class _InfoField extends StatelessWidget { + const _InfoField({ + required this.currentCurrency, + required this.receiveCurrency, + required this.exchangeRate, + required this.attachedAmount, + this.receiveBalance, + this.apy, + }); - return CommonInput( - titleChild: TonWalletIconWidget( - path: imagePath, - size: DimensSize.d16, - ), - titleText: LocaleKeys.tokenAmount.tr( - args: [currentBalance.currency.isoCode], - ), - controller: inputController, - hintText: '0.0', - inputFormatters: [ - CurrencyTextInputFormatter(currentBalance.currency), - ], - suffixIconConstraints: const BoxConstraints( - minHeight: commonInputHeight, - minWidth: DimensSize.d56, + final Currency currentCurrency; + final Currency receiveCurrency; + final double exchangeRate; + final Money attachedAmount; + final Money? receiveBalance; + final double? apy; + + @override + Widget build(BuildContext context) { + final theme = context.themeStyleV2; + + return PrimaryCard( + padding: const EdgeInsets.all(DimensSizeV2.d16), + borderRadius: BorderRadius.circular(DimensRadiusV2.radius16), + // color: color, + child: SeparatedColumn( + separatorSize: DimensSizeV2.d16, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _InfoRow( + label: LocaleKeys.exchangeRate.tr(), + child: Text( + // ignore: lines_longer_than_80_chars, no-magic-number, binary-expression-operand-order + '1 ${currentCurrency.isoCode} ≈ ${(1 * exchangeRate).toStringAsFixed(4)} ${receiveCurrency.isoCode}', + style: theme.textStyles.labelSmall, + overflow: TextOverflow.ellipsis, + softWrap: false, + maxLines: 1, + ), ), - suffixIcon: SmallButton( - onPressed: () => context - .read() - .add(const StakingBlocEvent.selectMax()), - buttonType: EverButtonType.ghost, - contentColor: colors.blue, - text: LocaleKeys.maxWord.tr(), + _InfoRow( + label: LocaleKeys.attachedAmount.tr(), + child: AmountWidget.fromMoney( + amount: attachedAmount, + ), ), - ); - }, + if (receiveBalance != null) + _InfoRow( + label: LocaleKeys.receiveWord.tr(), + child: AmountWidget.fromMoney( + amount: receiveBalance!, + sign: receiveBalance!.amount == Fixed.zero ? '' : '~', + ), + ), + if (apy != null) + _InfoRow( + label: LocaleKeys.averageApy.tr(), + child: Text( + '${apy!.toStringAsFixed(2)} %', + style: theme.textStyles.labelSmall, + overflow: TextOverflow.ellipsis, + softWrap: false, + maxLines: 1, + ), + ), + ], + ), ); } +} + +class _InfoRow extends StatelessWidget { + const _InfoRow({ + required this.label, + required this.child, + }); + + final String label; + final Widget child; + + @override + Widget build(BuildContext context) { + final theme = context.themeStyleV2; - Widget _infoField() { - return ShapedContainerColumn( - margin: EdgeInsets.zero, - padding: EdgeInsets.zero, + return SeparatedRow( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, children: [ - CommonListTile( - invertTitleSubtitleStyles: true, - titleText: LocaleKeys.exchangeRate.tr(), - subtitleText: - // ignore: lines_longer_than_80_chars, no-magic-number, binary-expression-operand-order - '1 ${currentBalance.currency.isoCode} ≈ ${(1 * (exchangeRate ?? 1)).toStringAsFixed(4)} ${receiveCurrency.isoCode}', - ), - CommonListTile( - invertTitleSubtitleStyles: true, - titleText: LocaleKeys.attachedAmount.tr(), - subtitleChild: MoneyWidget( - money: Money.fromBigIntWithCurrency( - attachedAmount, - Currencies()[inject() - .currentTransport - .nativeTokenTicker]!, - ), - style: MoneyWidgetStyle.primary, + Text( + label, + style: theme.textStyles.labelSmall.copyWith( + color: theme.colors.content3, ), ), - if (receiveBalance != null) - CommonListTile( - invertTitleSubtitleStyles: true, - titleText: LocaleKeys.receiveWord.tr(), - subtitleChild: MoneyWidget( - money: receiveBalance!, - style: MoneyWidgetStyle.primary, - signValue: receiveBalance!.amount == Fixed.zero ? '' : '~', - ), - ), - if (apy != null) - CommonListTile( - invertTitleSubtitleStyles: true, - titleText: LocaleKeys.averageApy.tr(), - // ignore: no-magic-number - subtitleText: '${apy!.toStringAsFixed(2)} %', - ), + Flexible(child: child), ], ); } diff --git a/lib/feature/wallet/staking/widgets/how_it_works_sheet.dart b/lib/feature/wallet/staking/widgets/how_it_works_sheet.dart index 99148075b..50c24999a 100644 --- a/lib/feature/wallet/staking/widgets/how_it_works_sheet.dart +++ b/lib/feature/wallet/staking/widgets/how_it_works_sheet.dart @@ -1,6 +1,7 @@ import 'package:app/generated/generated.dart'; import 'package:flutter/material.dart'; import 'package:ui_components_lib/ui_components_lib.dart'; +import 'package:ui_components_lib/v2/widgets/widgets.dart'; /// Helper function that displays [StEverHowItWorksSheet] Future showStEverHowItWorksSheet(BuildContext context) { @@ -8,6 +9,7 @@ Future showStEverHowItWorksSheet(BuildContext context) { context: context, useAppBackgroundColor: true, title: LocaleKeys.howLiquidStakingWorks.tr(), + centerTitle: true, body: (_, scrollController) => StEverHowItWorksSheet( scrollController: scrollController, ), @@ -28,51 +30,50 @@ class StEverHowItWorksSheet extends StatelessWidget { return SeparatedColumn( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, - separatorSize: DimensSize.d16, + separatorSize: DimensSizeV2.d16, children: [ Flexible( child: SingleChildScrollView( controller: scrollController, - child: ShapedContainerColumn( - mainAxisSize: MainAxisSize.min, - margin: EdgeInsets.zero, - padding: const EdgeInsets.symmetric(vertical: DimensSize.d16), - separator: const Padding( - padding: EdgeInsets.symmetric(vertical: DimensSize.d16), - child: CommonDivider(), - ), - children: [ - _step( - icon: Assets.images.ever.svg( - width: DimensSize.d40, - height: DimensSize.d40, + child: PrimaryCard( + padding: EdgeInsets.zero, + borderRadius: BorderRadius.circular(DimensRadiusV2.radius12), + child: SeparatedColumn( + mainAxisSize: MainAxisSize.min, + separator: const CommonDivider(), + children: [ + _step( + icon: Assets.images.ever.svg( + width: DimensSizeV2.d40, + height: DimensSizeV2.d40, + ), + title: LocaleKeys.stakeEverTitle.tr(), + subtitle: LocaleKeys.stakeEverSubtitle.tr(), ), - title: LocaleKeys.stakeEverTitle.tr(), - subtitle: LocaleKeys.stakeEverSubtitle.tr(), - ), - _step( - icon: Assets.images.stever.stever.svg( - width: DimensSize.d40, - height: DimensSize.d40, + _step( + icon: Assets.images.stever.stever.svg( + width: DimensSizeV2.d40, + height: DimensSizeV2.d40, + ), + title: LocaleKeys.receiveSteverTitle.tr(), + subtitle: LocaleKeys.receiveSteverSubtitle.tr(), ), - title: LocaleKeys.receiveSteverTitle.tr(), - subtitle: LocaleKeys.receiveSteverSubtitle.tr(), - ), - _step( - icon: Assets.images.stever.steverDefi.svg( - width: DimensSize.d40, - height: DimensSize.d40, + _step( + icon: Assets.images.stever.steverDefi.svg( + width: DimensSizeV2.d40, + height: DimensSizeV2.d40, + ), + title: LocaleKeys.useSteverTitle.tr(), + subtitle: LocaleKeys.useSteverSubtitle.tr(), ), - title: LocaleKeys.useSteverTitle.tr(), - subtitle: LocaleKeys.useSteverSubtitle.tr(), - ), - ], + ], + ), ), ), ), - CommonButton.primary( - fillWidth: true, - text: LocaleKeys.gotIt.tr(), + PrimaryButton( + buttonShape: ButtonShape.pill, + title: LocaleKeys.gotIt.tr(), onPressed: () => Navigator.of(context).pop(), ), ], @@ -86,33 +87,29 @@ class StEverHowItWorksSheet extends StatelessWidget { }) { return Builder( builder: (context) { - final colors = context.themeStyle.colors; + final theme = context.themeStyleV2; return Padding( - padding: const EdgeInsets.symmetric(horizontal: DimensSize.d16), + padding: const EdgeInsets.all(DimensSizeV2.d16), child: SeparatedRow( crossAxisAlignment: CrossAxisAlignment.start, - separatorSize: DimensSize.d12, + separatorSize: DimensSizeV2.d12, children: [ icon, Expanded( child: SeparatedColumn( - separatorSize: DimensSize.d4, + separatorSize: DimensSizeV2.d4, mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( title, - style: StyleRes.primaryRegular.copyWith( - color: colors.textPrimary, - ), + style: theme.textStyles.headingSmall, ), const SizedBox(height: 4), Text( subtitle, - style: StyleRes.secondaryRegular.copyWith( - color: colors.textPrimary, - ), + style: theme.textStyles.paragraphSmall, ), ], ), diff --git a/lib/feature/wallet/staking/widgets/staking_in_progress.dart b/lib/feature/wallet/staking/widgets/staking_in_progress.dart index 1e61df539..f506654f1 100644 --- a/lib/feature/wallet/staking/widgets/staking_in_progress.dart +++ b/lib/feature/wallet/staking/widgets/staking_in_progress.dart @@ -20,6 +20,8 @@ class StakingInProgress extends StatelessWidget { required this.withdrawHours, required this.stakeCurrency, required this.attachedFee, + required this.tokenPrice, + required this.everPrice, super.key, }); @@ -30,6 +32,8 @@ class StakingInProgress extends StatelessWidget { final int withdrawHours; final Currency stakeCurrency; final BigInt attachedFee; + final String tokenPrice; + final String everPrice; @override Widget build(BuildContext context) { @@ -59,6 +63,8 @@ class StakingInProgress extends StatelessWidget { stakeCurrency.isoCode, walletCancelUnstakingAttachedFeeQueryParam: attachedFee.toString(), + walletCancelUnstakingTokenPriceQueryParam: tokenPrice, + walletCancelUnstakingEverPriceQueryParam: everPrice, }, ), ), diff --git a/lib/feature/wallet/staking/widgets/verify_cancel_unstaking_sheet.dart b/lib/feature/wallet/staking/widgets/verify_cancel_unstaking_sheet.dart index 78967b3ed..47db15e92 100644 --- a/lib/feature/wallet/staking/widgets/verify_cancel_unstaking_sheet.dart +++ b/lib/feature/wallet/staking/widgets/verify_cancel_unstaking_sheet.dart @@ -1,6 +1,7 @@ import 'package:app/generated/generated.dart'; import 'package:flutter/widgets.dart'; import 'package:ui_components_lib/ui_components_lib.dart'; +import 'package:ui_components_lib/v2/widgets/widgets.dart'; /// Helper function that shows [VerifyCancelUnstakingSheet]. /// Returns true if user decided to cancel unstaking. @@ -8,6 +9,7 @@ Future showVerifyCancelUnstakingSheet(BuildContext context) async { final res = await showCommonBottomSheet( context: context, title: LocaleKeys.cancelUnstakingCheckTitle.tr(), + centerTitle: true, body: (_, __) => const VerifyCancelUnstakingSheet(), ); @@ -19,19 +21,21 @@ class VerifyCancelUnstakingSheet extends StatelessWidget { @override Widget build(BuildContext context) { + final theme = context.themeStyleV2; + return SeparatedColumn( - separatorSize: DimensSize.d16, + separatorSize: DimensSizeV2.d24, mainAxisSize: MainAxisSize.min, children: [ Text( LocaleKeys.cancelUnstakingCheckSubtitle.tr(), - style: StyleRes.primaryRegular.copyWith( - color: context.themeStyle.colors.textPrimary, + style: theme.textStyles.paragraphMedium.copyWith( + color: theme.colors.content3, ), ), - CommonButton.primary( - fillWidth: true, - text: LocaleKeys.yesImSure.tr(), + PrimaryButton( + buttonShape: ButtonShape.pill, + title: LocaleKeys.yesImSure.tr(), onPressed: () => Navigator.of(context).pop(true), ), ], diff --git a/lib/feature/wallet/wallet_prepare_transfer/wallet_prepare_transfer_page/data/wallet_prepare_transfer_asset.dart b/lib/feature/wallet/wallet_prepare_transfer/wallet_prepare_transfer_page/data/wallet_prepare_transfer_asset.dart index 7f02f61fa..b927cc913 100644 --- a/lib/feature/wallet/wallet_prepare_transfer/wallet_prepare_transfer_page/data/wallet_prepare_transfer_asset.dart +++ b/lib/feature/wallet/wallet_prepare_transfer/wallet_prepare_transfer_page/data/wallet_prepare_transfer_asset.dart @@ -1,48 +1,22 @@ import 'package:app/data/models/models.dart'; -import 'package:equatable/equatable.dart'; +import 'package:app/widgets/amount_input/amount_input_asset.dart'; import 'package:flutter/foundation.dart'; import 'package:nekoton_repository/nekoton_repository.dart'; /// Model that describes asset during PrepareTransfer step. @immutable -class WalletPrepareTransferAsset extends Equatable { +class WalletPrepareTransferAsset extends AmountInputAsset { const WalletPrepareTransferAsset({ - required this.rootTokenContract, - required this.isNative, - required this.balance, - required this.logoURI, - required this.title, - required this.tokenSymbol, - this.version, - this.currency, + required super.rootTokenContract, + required super.isNative, + required super.balance, + required super.logoURI, + required super.title, + required super.tokenSymbol, + super.version, + super.currency, }); - /// Contract of token. - /// For native token is specified, but [isNative] is true - final Address rootTokenContract; - - /// If this asset is native token. - /// If true, then [rootTokenContract] will be useless. - final bool isNative; - - /// Balance of this token. - final Money balance; - - /// Symbol of token (ticker), that is used for selection - final String tokenSymbol; - - /// Svg path or network uri - final String? logoURI; - - /// Title of token - final String title; - - /// Version of TokenWallet. - /// Null for native token - final TokenWalletVersion? version; - - final CustomCurrency? currency; - /// Copy asset with new balance WalletPrepareTransferAsset copyWith({ Money? balance, @@ -58,9 +32,4 @@ class WalletPrepareTransferAsset extends Equatable { tokenSymbol: tokenSymbol, currency: currency ?? this.currency, ); - - @override - List get props => [rootTokenContract, balance, tokenSymbol]; - - (Address, String) get key => (rootTokenContract, tokenSymbol); } diff --git a/lib/feature/wallet/wallet_prepare_transfer/wallet_prepare_transfer_page/wallet_prepare_transfer_page_wm.dart b/lib/feature/wallet/wallet_prepare_transfer/wallet_prepare_transfer_page/wallet_prepare_transfer_page_wm.dart index 9f9d17b6d..0fed6e6f4 100644 --- a/lib/feature/wallet/wallet_prepare_transfer/wallet_prepare_transfer_page/wallet_prepare_transfer_page_wm.dart +++ b/lib/feature/wallet/wallet_prepare_transfer/wallet_prepare_transfer_page/wallet_prepare_transfer_page_wm.dart @@ -16,6 +16,7 @@ import 'package:app/feature/wallet/wallet_prepare_transfer/wallet_prepare_transf import 'package:app/feature/wallet/wallet_prepare_transfer/wallet_prepare_transfer_page/wallet_prepare_transfer_page.dart'; import 'package:app/feature/wallet/wallet_prepare_transfer/wallet_prepare_transfer_page/wallet_prepare_transfer_page_model.dart'; import 'package:app/generated/generated.dart'; +import 'package:app/widgets/amount_input/amount_input_asset.dart'; import 'package:elementary/elementary.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; @@ -106,7 +107,7 @@ class WalletPrepareTransferPageWidgetModel extends CustomWidgetModel< String? getSeedName(PublicKey custodian) => model.getSeedName(custodian); - void onChangeAsset(WalletPrepareTransferAsset newAsset) { + void onChangeAsset(AmountInputAsset newAsset) { final asset = _assets[newAsset.key]; if (_selectedAsset?.rootTokenContract == newAsset.rootTokenContract && _selectedAsset?.tokenSymbol == newAsset.tokenSymbol || diff --git a/lib/feature/wallet/wallet_prepare_transfer/wallet_prepare_transfer_page/widgets/wallet_prepare_transfer_view.dart b/lib/feature/wallet/wallet_prepare_transfer/wallet_prepare_transfer_page/widgets/wallet_prepare_transfer_view.dart index 4180833dc..dada7e9db 100644 --- a/lib/feature/wallet/wallet_prepare_transfer/wallet_prepare_transfer_page/widgets/wallet_prepare_transfer_view.dart +++ b/lib/feature/wallet/wallet_prepare_transfer/wallet_prepare_transfer_page/widgets/wallet_prepare_transfer_view.dart @@ -1,8 +1,8 @@ import 'package:app/feature/wallet/wallet.dart'; import 'package:app/feature/wallet/wallet_prepare_transfer/wallet_prepare_transfer_page/data/wallet_prepare_transfer_asset.dart'; import 'package:app/feature/wallet/wallet_prepare_transfer/wallet_prepare_transfer_page/wallet_prepare_transfer_page_wm.dart'; -import 'package:app/feature/wallet/wallet_prepare_transfer/wallet_prepare_transfer_page/widgets/wallet_prepare_transfer_amount_input.dart'; import 'package:app/generated/generated.dart'; +import 'package:app/widgets/amount_input/amount_input.dart'; import 'package:elementary_helper/elementary_helper.dart'; import 'package:flutter/material.dart'; import 'package:lucide_icons_flutter/lucide_icons.dart'; @@ -64,7 +64,7 @@ class WalletPrepareTransferView extends StatelessWidget { validator: _wm.validateAddressField, suffixes: [_buildReceiverSuffix()], ), - WalletPrepareTransferAmountInput( + AmountInput( controller: _wm.amountController, focusNode: _wm.amountFocus, assets: _wm.assets, diff --git a/lib/feature/wallet/widgets/account_transactions_tab/detail/details_body.dart b/lib/feature/wallet/widgets/account_transactions_tab/detail/details_body.dart index 8560545f0..964b6b834 100644 --- a/lib/feature/wallet/widgets/account_transactions_tab/detail/details_body.dart +++ b/lib/feature/wallet/widgets/account_transactions_tab/detail/details_body.dart @@ -181,14 +181,3 @@ class WalletTransactionDetailsDefaultBody extends StatelessWidget { ); } } - -extension on Money { - Money exchangeToUSD(Fixed price) => exchangeTo( - ExchangeRate.fromFixed( - price, - fromIsoCode: currency.isoCode, - toIsoCode: 'USD', - toDecimalDigits: 10, - ), - ); -} diff --git a/lib/feature/wallet/widgets/token_transfer_info/token_transfer_info_wm.dart b/lib/feature/wallet/widgets/token_transfer_info/token_transfer_info_wm.dart index 510aa4653..06b495f37 100644 --- a/lib/feature/wallet/widgets/token_transfer_info/token_transfer_info_wm.dart +++ b/lib/feature/wallet/widgets/token_transfer_info/token_transfer_info_wm.dart @@ -4,6 +4,7 @@ import 'package:app/data/models/models.dart'; import 'package:app/di/di.dart'; import 'package:app/feature/wallet/widgets/token_transfer_info/token_transfer_info_model.dart'; import 'package:app/feature/wallet/widgets/token_transfer_info/token_transfer_info_widget.dart'; +import 'package:app/utils/utils.dart'; import 'package:elementary_helper/elementary_helper.dart'; import 'package:flutter/material.dart'; import 'package:nekoton_repository/nekoton_repository.dart'; @@ -113,14 +114,3 @@ class TokenTransferInfoWidgetModel _tokenAsset.accept(tokenAsset); } } - -extension on Money { - Money exchangeToUSD(Fixed price) => exchangeTo( - ExchangeRate.fromFixed( - price, - fromIsoCode: currency.isoCode, - toIsoCode: 'USD', - toDecimalDigits: 2, - ), - ); -} diff --git a/lib/feature/wallet/widgets/wallet_account_actions/wallet_account_actions.dart b/lib/feature/wallet/widgets/wallet_account_actions/wallet_account_actions.dart index 4c27cd21c..20a480a53 100644 --- a/lib/feature/wallet/widgets/wallet_account_actions/wallet_account_actions.dart +++ b/lib/feature/wallet/widgets/wallet_account_actions/wallet_account_actions.dart @@ -118,25 +118,23 @@ class _ActionList extends StatelessWidget { icon: _actionIcon(action), onPressed: account?.let((_) => _actionOnPressed(context)), ), - // TODO(knightforce): temp - // if (hasStake) - // WalletActionButton( - // label: LocaleKeys.stakeWord.tr(), - // icon: LucideIcons.layers2, - // badge: hasStakeActions, - // onPressed: account?.let( - // (account) => () { - // context.goFurther( - // AppRoute.walletStake.pathWithData( - // pathParameters: { - // walletStakeAddressPathParam: - // account.address.address, - // }, - // ), - // ); - // }, - // ), - // ), + if (hasStake) + WalletActionButton( + label: LocaleKeys.stakeWord.tr(), + icon: LucideIcons.layers2, + badge: hasStakeActions, + onPressed: account?.let( + (account) => () { + context.goFurther( + AppRoute.walletStake.pathWithData( + pathParameters: { + walletStakeAddressPathParam: account.address.address, + }, + ), + ); + }, + ), + ), ], ), ), diff --git a/lib/utils/utils.dart b/lib/utils/utils.dart index 5251b662c..90c2776a2 100644 --- a/lib/utils/utils.dart +++ b/lib/utils/utils.dart @@ -83,3 +83,14 @@ String toEllipseString(String value) => value.length > 6 extension FunctionalExt on T { R let(R Function(T value) block) => block(this); } + +extension MoneyExt on Money { + Money exchangeToUSD(Fixed price) => exchangeTo( + ExchangeRate.fromFixed( + price, + fromIsoCode: currency.isoCode, + toIsoCode: 'USD', + toDecimalDigits: 2, + ), + ); +} diff --git a/lib/feature/wallet/wallet_prepare_transfer/wallet_prepare_transfer_page/widgets/wallet_prepare_transfer_amount_input.dart b/lib/widgets/amount_input/amount_input.dart similarity index 85% rename from lib/feature/wallet/wallet_prepare_transfer/wallet_prepare_transfer_page/widgets/wallet_prepare_transfer_amount_input.dart rename to lib/widgets/amount_input/amount_input.dart index 2098a84ea..62fc6012d 100644 --- a/lib/feature/wallet/wallet_prepare_transfer/wallet_prepare_transfer_page/widgets/wallet_prepare_transfer_amount_input.dart +++ b/lib/widgets/amount_input/amount_input.dart @@ -1,7 +1,7 @@ -import 'package:app/feature/wallet/wallet_prepare_transfer/wallet_prepare_transfer_page/data/wallet_prepare_transfer_asset.dart'; -import 'package:app/feature/wallet/wallet_prepare_transfer/wallet_prepare_transfer_page/widgets/wallet_prepare_transfer_asset_select.dart'; import 'package:app/generated/generated.dart'; import 'package:app/utils/utils.dart'; +import 'package:app/widgets/amount_input/amount_input_asset.dart'; +import 'package:app/widgets/amount_input/amount_input_asset_select.dart'; import 'package:auto_size_text_field/auto_size_text_field.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; @@ -9,39 +9,31 @@ import 'package:string_extensions/string_extensions.dart'; import 'package:ui_components_lib/ui_components_lib.dart'; import 'package:ui_components_lib/v2/ui_components_lib_v2.dart'; -class WalletPrepareTransferAmountInput extends StatefulWidget { - const WalletPrepareTransferAmountInput({ - required this.focusNode, +class AmountInput extends StatefulWidget { + const AmountInput({ required this.controller, required this.selectedAsset, - required this.assets, - required this.onSelectedAssetChanged, required this.onMaxAmount, required this.onSubmitted, + this.focusNode, + this.assets, + this.onSelectedAssetChanged, super.key, }); - final FocusNode focusNode; - + final FocusNode? focusNode; final TextEditingController controller; - - final ValueListenable> assets; - - final WalletPrepareTransferAsset? selectedAsset; - - final ValueChanged onSelectedAssetChanged; - + final ValueListenable>? assets; + final AmountInputAsset? selectedAsset; + final ValueChanged? onSelectedAssetChanged; final VoidCallback onMaxAmount; - final ValueChanged onSubmitted; @override - State createState() => - _WalletPrepareTransferAmountInputState(); + State createState() => _AmountInputState(); } -class _WalletPrepareTransferAmountInputState - extends State { +class _AmountInputState extends State { CurrencyTextInputFormatter? formatter; CurrencyTextInputValidator? validator; @@ -52,7 +44,7 @@ class _WalletPrepareTransferAmountInputState } @override - void didUpdateWidget(covariant WalletPrepareTransferAmountInput oldWidget) { + void didUpdateWidget(covariant AmountInput oldWidget) { if (oldWidget.selectedAsset != widget.selectedAsset) { _updateFormatterValidator(); } @@ -86,7 +78,7 @@ class _WalletPrepareTransferAmountInputState separatorSize: DimensSizeV2.d4, crossAxisAlignment: CrossAxisAlignment.start, children: [ - WalletPrepareTransferAssetSelect( + AmountInputAssetSelect( values: widget.assets, currentValue: widget.selectedAsset, onChanged: widget.onSelectedAssetChanged, @@ -120,7 +112,7 @@ class _WalletPrepareTransferAmountInputState ), child: GestureDetector( behavior: HitTestBehavior.translucent, - onTap: widget.focusNode.requestFocus, + onTap: widget.focusNode?.requestFocus, child: FormField( initialValue: widget.controller.text, autovalidateMode: AutovalidateMode.onUserInteraction, diff --git a/lib/widgets/amount_input/amount_input_asset.dart b/lib/widgets/amount_input/amount_input_asset.dart new file mode 100644 index 000000000..77da4702a --- /dev/null +++ b/lib/widgets/amount_input/amount_input_asset.dart @@ -0,0 +1,49 @@ +import 'package:app/data/models/models.dart'; +import 'package:equatable/equatable.dart'; +import 'package:flutter/foundation.dart'; +import 'package:nekoton_repository/nekoton_repository.dart'; + +@immutable +class AmountInputAsset extends Equatable { + const AmountInputAsset({ + required this.rootTokenContract, + required this.isNative, + required this.balance, + required this.logoURI, + required this.title, + required this.tokenSymbol, + this.version, + this.currency, + }); + + /// Contract of token. + /// For native token is specified, but [isNative] is true + final Address rootTokenContract; + + /// If this asset is native token. + /// If true, then [rootTokenContract] will be useless. + final bool isNative; + + /// Balance of this token. + final Money balance; + + /// Symbol of token (ticker), that is used for selection + final String tokenSymbol; + + /// Svg path or network uri + final String? logoURI; + + /// Title of token + final String title; + + /// Version of TokenWallet. + /// Null for native token + final TokenWalletVersion? version; + + final CustomCurrency? currency; + + @override + List get props => [rootTokenContract, balance, tokenSymbol]; + + (Address, String) get key => (rootTokenContract, tokenSymbol); +} diff --git a/lib/feature/wallet/wallet_prepare_transfer/wallet_prepare_transfer_page/widgets/wallet_prepare_transfer_asset_select.dart b/lib/widgets/amount_input/amount_input_asset_select.dart similarity index 87% rename from lib/feature/wallet/wallet_prepare_transfer/wallet_prepare_transfer_page/widgets/wallet_prepare_transfer_asset_select.dart rename to lib/widgets/amount_input/amount_input_asset_select.dart index 0bf823a77..da90f3e14 100644 --- a/lib/feature/wallet/wallet_prepare_transfer/wallet_prepare_transfer_page/widgets/wallet_prepare_transfer_asset_select.dart +++ b/lib/widgets/amount_input/amount_input_asset_select.dart @@ -1,6 +1,6 @@ -import 'package:app/feature/wallet/wallet_prepare_transfer/wallet_prepare_transfer_page/data/wallet_prepare_transfer_asset.dart'; import 'package:app/feature/wallet/widgets/account_asset_tab/token_wallet_asset/token_wallet_icon.dart'; import 'package:app/generated/generated.dart'; +import 'package:app/widgets/amount_input/amount_input_asset.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:lucide_icons_flutter/lucide_icons.dart'; @@ -8,26 +8,26 @@ import 'package:nekoton_repository/nekoton_repository.dart'; import 'package:ui_components_lib/ui_components_lib.dart'; import 'package:ui_components_lib/v2/ui_components_lib_v2.dart'; -class WalletPrepareTransferAssetSelect extends StatelessWidget { - const WalletPrepareTransferAssetSelect({ - required this.values, +class AmountInputAssetSelect extends StatelessWidget { + const AmountInputAssetSelect({ required this.currentValue, - required this.onChanged, + this.values, + this.onChanged, super.key, }); - final ValueListenable> values; + final ValueListenable>? values; - final WalletPrepareTransferAsset? currentValue; + final AmountInputAsset? currentValue; - final ValueChanged onChanged; + final ValueChanged? onChanged; @override Widget build(BuildContext context) { final theme = context.themeStyleV2; return PressScaleWidget( - onPressed: () => _openSelectSheet(context), + onPressed: values != null ? () => _openSelectSheet(context) : null, child: SeparatedRow( children: [ if (currentValue != null) ...[ @@ -58,7 +58,7 @@ class WalletPrepareTransferAssetSelect extends StatelessWidget { } Widget _itemBuilder( - WalletPrepareTransferAsset asset, { + AmountInputAsset asset, { bool isSelected = false, VoidCallback? onPressed, }) => @@ -121,7 +121,7 @@ class WalletPrepareTransferAssetSelect extends StatelessWidget { controller: scrollController, padding: const EdgeInsets.only(top: DimensSizeV2.d16), child: ValueListenableBuilder( - valueListenable: values, + valueListenable: values!, builder: (_, values, __) => SeparatedColumn( separatorSize: DimensSizeV2.d12, children: values @@ -131,7 +131,7 @@ class WalletPrepareTransferAssetSelect extends StatelessWidget { isSelected: asset == currentValue, onPressed: () { Navigator.of(context).pop(); - onChanged(asset); + onChanged?.call(asset); }, ), ) diff --git a/packages/ui_components_lib/lib/v2/widgets/segment_control/primary_segment_control.dart b/packages/ui_components_lib/lib/v2/widgets/segment_control/primary_segment_control.dart index 52627ecef..a8e3d08e2 100644 --- a/packages/ui_components_lib/lib/v2/widgets/segment_control/primary_segment_control.dart +++ b/packages/ui_components_lib/lib/v2/widgets/segment_control/primary_segment_control.dart @@ -12,13 +12,18 @@ class PrimarySegmentControl extends StatelessWidget { this.icon, this.size = SegmentControlSize.large, this.title, + this.titleSpan, super.key, - }); + }) : assert( + title == null || titleSpan == null, + 'title and titleSpan cant be provided simultaneously', + ); final SegmentControlSize size; final SegmentControlState state; final IconData? icon; final String? title; + final InlineSpan? titleSpan; final T value; double get _iconSize { @@ -76,6 +81,7 @@ class PrimarySegmentControl extends StatelessWidget { SegmentControlState? state, IconData? icon, String? title, + InlineSpan? titleSpan, T? value, }) { return PrimarySegmentControl( @@ -83,6 +89,7 @@ class PrimarySegmentControl extends StatelessWidget { state: state ?? this.state, icon: icon ?? this.icon, title: title ?? this.title, + titleSpan: titleSpan ?? this.titleSpan, value: value ?? this.value, ); } @@ -97,7 +104,7 @@ class PrimarySegmentControl extends StatelessWidget { borderRadius: BorderRadius.circular(_borderRadius), color: style.backgroundColor, ), - child: Row( + child: SeparatedRow( mainAxisAlignment: MainAxisAlignment.center, children: [ if (icon != null) @@ -106,16 +113,9 @@ class PrimarySegmentControl extends StatelessWidget { color: style.iconColor, size: _iconSize, ), - if (title != null) - Padding( - padding: icon != null - ? const EdgeInsets.only(left: DimensSize.d8) - : EdgeInsets.zero, - child: Text( - title!, - style: style.titleTextStyle, - ), - ), + if (title != null) Text(title!, style: style.titleTextStyle), + if (titleSpan != null) + Text.rich(titleSpan!, style: style.titleTextStyle), ], ), ); diff --git a/packages/ui_components_lib/lib/v2/widgets/segment_control/switcher_segment_controls.dart b/packages/ui_components_lib/lib/v2/widgets/segment_control/switcher_segment_controls.dart index 67d122921..c4c673c12 100644 --- a/packages/ui_components_lib/lib/v2/widgets/segment_control/switcher_segment_controls.dart +++ b/packages/ui_components_lib/lib/v2/widgets/segment_control/switcher_segment_controls.dart @@ -7,11 +7,13 @@ class SwitcherSegmentControls extends StatelessWidget { required this.currentValue, required this.values, required this.onTabChanged, + this.fullWidth = true, super.key, }); final T currentValue; final List> values; + final bool fullWidth; final ValueChanged onTabChanged; @override @@ -24,9 +26,11 @@ class SwitcherSegmentControls extends StatelessWidget { ), padding: const EdgeInsets.all(DimensSizeV2.d4), child: Row( + mainAxisSize: fullWidth ? MainAxisSize.max : MainAxisSize.min, children: [ for (final segment in values) - Expanded( + Flexible( + fit: fullWidth ? FlexFit.tight : FlexFit.loose, child: GestureDetector( onTap: () => onTabChanged(segment.value), child: segment.copyWith(