mirror of
https://github.com/Such-Software/hash-wallet.git
synced 2026-10-26 06:08:40 +01:00
Merge branch 'refs/heads/dev' into cw-1440-new-buy-sell-flow
# Conflicts: # lib/new-ui/widgets/coins_page/cards/cards_view.dart # lib/new-ui/widgets/receive_page/receive_top_bar.dart
This commit is contained in:
@@ -141,6 +141,7 @@ class AuthPagePinCodeStateImpl extends AuthPageState<AuthPage> {
|
||||
size: 36,
|
||||
icon: Icon(Icons.close),
|
||||
onPressed: Navigator.of(context).pop,
|
||||
semanticLabel: S.of(context).close,
|
||||
iconColor: Theme.of(context).colorScheme.onSurfaceVariant)
|
||||
: Container(),
|
||||
backgroundColor: Theme.of(context).colorScheme.surface,
|
||||
|
||||
@@ -36,6 +36,7 @@ class _EditBackupPasswordPageState extends State<EditBackupPasswordPage> {
|
||||
ModalTopBar(
|
||||
title: S.of(context).edit_backup_password,
|
||||
leadingIcon: Icon(Icons.arrow_back_ios_new),
|
||||
leadingSemanticLabel: S.of(context).seed_alert_back,
|
||||
onLeadingPressed: Navigator.of(context).pop,
|
||||
),
|
||||
Expanded(
|
||||
|
||||
@@ -84,20 +84,16 @@ abstract class BasePage extends StatelessWidget {
|
||||
return null;
|
||||
}
|
||||
|
||||
return MergeSemantics(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(top: 4.0),
|
||||
child: ButtonTheme(
|
||||
minWidth: double.minPositive,
|
||||
child: Semantics(
|
||||
label: S.of(context).seed_alert_back,
|
||||
child: ModernButton(
|
||||
size: 37,
|
||||
icon: Icon(CupertinoIcons.back),
|
||||
iconColor: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
onPressed: () => onClose(context),
|
||||
),
|
||||
),
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(top: 4.0),
|
||||
child: ButtonTheme(
|
||||
minWidth: double.minPositive,
|
||||
child: ModernButton(
|
||||
size: 37,
|
||||
icon: Icon(CupertinoIcons.back),
|
||||
semanticLabel: S.of(context).seed_alert_back,
|
||||
iconColor: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
onPressed: () => onClose(context),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -42,7 +42,7 @@ class ConnectDevicePage extends BasePage {
|
||||
ConnectDevicePage(ConnectDevicePageParams params, this.hardwareWalletVM)
|
||||
: walletType = params.walletType,
|
||||
onConnectDevice = params.onConnectDevice,
|
||||
allowChangeWallet = params.allowChangeWallet,
|
||||
allowChangeWallet = params.allowChangeWallet || params.isReconnect,
|
||||
isReconnect = params.isReconnect;
|
||||
final WalletType walletType;
|
||||
final OnConnectDevice onConnectDevice;
|
||||
@@ -142,12 +142,22 @@ class ConnectDevicePageBodyState extends State<ConnectDevicePageBody> {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
var _isRefreshingUsb = false;
|
||||
Future<void> _refreshUsbDevices() async {
|
||||
final dev = await widget.hardwareWalletVM.getAllUsbDevices();
|
||||
|
||||
if (usbDevices.length != dev.length) {
|
||||
setState(() => usbDevices = dev);
|
||||
if (_isRefreshingUsb) {
|
||||
return;
|
||||
}
|
||||
_isRefreshingUsb = true;
|
||||
try {
|
||||
final dev = await widget.hardwareWalletVM.getAllUsbDevices();
|
||||
|
||||
if (usbDevices.length != dev.length) {
|
||||
setState(() => usbDevices = dev);
|
||||
}
|
||||
} catch(e) {
|
||||
printV(e);
|
||||
}
|
||||
_isRefreshingUsb = false;
|
||||
}
|
||||
|
||||
Future<void> _refreshBleDevices() async {
|
||||
|
||||
@@ -1,19 +1,22 @@
|
||||
import 'package:cake_wallet/core/wallet_name_validator.dart';
|
||||
import 'package:cw_core/generate_name.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
|
||||
import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
|
||||
import 'package:cake_wallet/src/widgets/blockchain_height_widget.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/src/widgets/scrollable_with_bottom_section.dart';
|
||||
import 'package:cake_wallet/utils/responsive_layout_util.dart';
|
||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_hardware_restore_view_model.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import "dart:async";
|
||||
|
||||
import "package:cake_wallet/core/wallet_name_validator.dart";
|
||||
import "package:cake_wallet/generated/i18n.dart";
|
||||
import "package:cake_wallet/src/screens/base_page.dart";
|
||||
import "package:cake_wallet/src/screens/connect_device/monero_hardware_wallet_passphrase_input.dart";
|
||||
import "package:cake_wallet/src/widgets/alert_with_one_action.dart";
|
||||
import "package:cake_wallet/src/widgets/base_text_form_field.dart";
|
||||
import "package:cake_wallet/src/widgets/blockchain_height_widget.dart";
|
||||
import "package:cake_wallet/src/widgets/primary_button.dart";
|
||||
import "package:cake_wallet/src/widgets/scrollable_with_bottom_section.dart";
|
||||
import "package:cake_wallet/utils/responsive_layout_util.dart";
|
||||
import "package:cake_wallet/utils/show_pop_up.dart";
|
||||
import "package:cake_wallet/view_model/wallet_hardware_restore_view_model.dart";
|
||||
import "package:cw_core/generate_name.dart";
|
||||
import "package:cw_core/wallet_type.dart";
|
||||
import "package:flutter/material.dart";
|
||||
import "package:flutter_mobx/flutter_mobx.dart";
|
||||
import "package:mobx/mobx.dart";
|
||||
|
||||
class MoneroHardwareWalletOptionsPage extends BasePage {
|
||||
MoneroHardwareWalletOptionsPage(this._walletHardwareRestoreVM);
|
||||
@@ -42,13 +45,15 @@ class _MoneroHardwareWalletOptionsFormState extends State<_MoneroHardwareWalletO
|
||||
: _formKey = GlobalKey<FormState>(),
|
||||
_blockchainHeightKey = GlobalKey<BlockchainHeightState>(),
|
||||
_blockHeightFocusNode = FocusNode(),
|
||||
_controller = TextEditingController();
|
||||
_walletNameController = TextEditingController(),
|
||||
_passphraseController = TextEditingController();
|
||||
|
||||
final GlobalKey<FormState> _formKey;
|
||||
final GlobalKey<BlockchainHeightState> _blockchainHeightKey;
|
||||
final FocusNode _blockHeightFocusNode;
|
||||
final WalletHardwareRestoreViewModel _walletHardwareRestoreVM;
|
||||
final TextEditingController _controller;
|
||||
final TextEditingController _walletNameController;
|
||||
final TextEditingController _passphraseController;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@@ -57,131 +62,159 @@ class _MoneroHardwareWalletOptionsFormState extends State<_MoneroHardwareWalletO
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: EdgeInsets.only(top: 24),
|
||||
child: ScrollableWithBottomSection(
|
||||
contentPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
|
||||
content: Center(
|
||||
child: ConstrainedBox(
|
||||
constraints:
|
||||
BoxConstraints(maxWidth: ResponsiveLayoutUtilBase.kDesktopMaxWidthConstraint),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 0),
|
||||
child: Form(
|
||||
key: _formKey,
|
||||
child: Stack(
|
||||
alignment: Alignment.centerRight,
|
||||
children: [
|
||||
BaseTextFormField(
|
||||
onChanged: (value) => _walletHardwareRestoreVM.name = value,
|
||||
controller: _controller,
|
||||
textStyle: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||||
fontSize: 20.0,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
placeholderTextStyle: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||||
fontSize: 18.0,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
hintText: S.of(context).wallet_name,
|
||||
suffixIcon: Semantics(
|
||||
label: S.of(context).generate_name,
|
||||
child: IconButton(
|
||||
onPressed: _onGenerateName,
|
||||
icon: Container(
|
||||
padding: const EdgeInsets.all(8),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(6.0),
|
||||
color: Theme.of(context).colorScheme.surfaceContainerHighest,
|
||||
Widget build(BuildContext context) => Padding(
|
||||
padding: const EdgeInsets.only(top: 24),
|
||||
child: ScrollableWithBottomSection(
|
||||
contentPadding: const EdgeInsets.only(left: 24, right: 24, bottom: 24),
|
||||
content: Center(
|
||||
child: ConstrainedBox(
|
||||
constraints: const BoxConstraints(
|
||||
maxWidth: ResponsiveLayoutUtilBase.kDesktopMaxWidthConstraint,
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Padding(
|
||||
padding: EdgeInsets.zero,
|
||||
child: Form(
|
||||
key: _formKey,
|
||||
child: Stack(
|
||||
alignment: Alignment.centerRight,
|
||||
children: [
|
||||
BaseTextFormField(
|
||||
onChanged: (value) => _walletHardwareRestoreVM.name = value,
|
||||
controller: _walletNameController,
|
||||
textStyle: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
width: 34,
|
||||
height: 34,
|
||||
child: Image.asset(
|
||||
'assets/images/refresh_icon.png',
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
placeholderTextStyle: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
hintText: S.of(context).wallet_name,
|
||||
suffixIcon: Semantics(
|
||||
label: S.of(context).generate_name,
|
||||
child: IconButton(
|
||||
onPressed: _onGenerateName,
|
||||
icon: Container(
|
||||
padding: const EdgeInsets.all(8),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
color: Theme.of(context).colorScheme.surfaceContainerHighest,
|
||||
),
|
||||
width: 34,
|
||||
height: 34,
|
||||
child: Image.asset(
|
||||
"assets/images/refresh_icon.png",
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
validator: WalletNameValidator(),
|
||||
),
|
||||
validator: WalletNameValidator(),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 20),
|
||||
child: BlockchainHeightWidget(
|
||||
focusNode: _blockHeightFocusNode,
|
||||
key: _blockchainHeightKey,
|
||||
hasDatePicker: true,
|
||||
walletType: WalletType.monero,
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 20),
|
||||
child: BlockchainHeightWidget(
|
||||
focusNode: _blockHeightFocusNode,
|
||||
key: _blockchainHeightKey,
|
||||
hasDatePicker: true,
|
||||
walletType: WalletType.monero,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
bottomSectionPadding: const EdgeInsets.all(24),
|
||||
bottomSection: Observer(
|
||||
builder: (_) => Column(
|
||||
children: [
|
||||
if (_walletHardwareRestoreVM.passphraseAvailable) ...[
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
backgroundColor: Colors.transparent,
|
||||
builder: (context) => MoneroHardwareWalletPassphraseInputModal(
|
||||
controller: _passphraseController,
|
||||
),
|
||||
);
|
||||
},
|
||||
child: Text(S.of(context).add_a_passphrase),
|
||||
),
|
||||
],
|
||||
LoadingPrimaryButton(
|
||||
onPressed: _confirmForm,
|
||||
text: S.of(context).seed_language_next,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
textColor: Theme.of(context).colorScheme.onPrimary,
|
||||
isDisabled: _walletHardwareRestoreVM.name.isEmpty,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
bottomSectionPadding: EdgeInsets.all(24),
|
||||
bottomSection: Observer(
|
||||
builder: (context) => LoadingPrimaryButton(
|
||||
onPressed: _confirmForm,
|
||||
text: S.of(context).seed_language_next,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
textColor: Theme.of(context).colorScheme.onPrimary,
|
||||
isDisabled: _walletHardwareRestoreVM.name.isEmpty,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
Future<void> _onGenerateName() async {
|
||||
final rName = await generateName();
|
||||
FocusManager.instance.primaryFocus?.unfocus();
|
||||
|
||||
setState(() {
|
||||
_controller.text = rName;
|
||||
_walletNameController.text = rName;
|
||||
_walletHardwareRestoreVM.name = rName;
|
||||
_controller.selection =
|
||||
TextSelection.fromPosition(TextPosition(offset: _controller.text.length));
|
||||
_walletNameController.selection =
|
||||
TextSelection.fromPosition(TextPosition(offset: _walletNameController.text.length));
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _confirmForm() async {
|
||||
showPopUp<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) => AlertWithOneAction(
|
||||
alertTitle: S.of(context).proceed_on_device,
|
||||
alertContent: S.of(context).proceed_on_device_description,
|
||||
buttonText: S.of(context).cancel,
|
||||
alertBarrierDismissible: false,
|
||||
buttonAction: () => Navigator.of(context).pop(),
|
||||
unawaited(
|
||||
showPopUp<void>(
|
||||
context: context,
|
||||
builder: (context) => AlertWithOneAction(
|
||||
alertTitle: S.of(context).proceed_on_device,
|
||||
alertContent: S.of(context).proceed_on_device_description,
|
||||
buttonText: S.of(context).cancel,
|
||||
alertBarrierDismissible: false,
|
||||
buttonAction: () => Navigator.of(context).pop(),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
final options = {'height': _blockchainHeightKey.currentState?.height ?? -1};
|
||||
final options = <String, dynamic>{"height": _blockchainHeightKey.currentState?.height ?? -1};
|
||||
|
||||
if (_walletHardwareRestoreVM.passphraseAvailable && _passphraseController.text.isNotEmpty) {
|
||||
options["passphrase"] = _passphraseController.text;
|
||||
}
|
||||
await _walletHardwareRestoreVM.create(options: options);
|
||||
}
|
||||
|
||||
bool _effectsInstalled = false;
|
||||
|
||||
void _setEffects(BuildContext context) {
|
||||
if (_effectsInstalled) return;
|
||||
if (_effectsInstalled) {
|
||||
return;
|
||||
}
|
||||
|
||||
reaction((_) => _walletHardwareRestoreVM.error, (String? error) {
|
||||
reaction((_) => _walletHardwareRestoreVM.error, (error) {
|
||||
if (error != null) {
|
||||
if (error == S.current.ledger_connection_error) Navigator.of(context).pop();
|
||||
if (error == S.current.ledger_connection_error) {
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
showPopUp<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) => AlertWithOneAction(
|
||||
builder: (context) => AlertWithOneAction(
|
||||
alertTitle: S.of(context).error,
|
||||
alertContent: error,
|
||||
buttonText: S.of(context).ok,
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
import "package:cake_wallet/generated/i18n.dart";
|
||||
import "package:cake_wallet/new-ui/widgets/new_primary_button.dart";
|
||||
import "package:cake_wallet/new-ui/widgets/receive_page/receive_top_bar.dart";
|
||||
import "package:cake_wallet/src/widgets/base_text_form_field.dart";
|
||||
import "package:flutter/material.dart";
|
||||
|
||||
class MoneroHardwareWalletPassphraseInputModal extends StatelessWidget {
|
||||
const MoneroHardwareWalletPassphraseInputModal({required this.controller, super.key});
|
||||
|
||||
final TextEditingController controller;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => Padding(
|
||||
padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: const BorderRadius.vertical(top: Radius.circular(18)),
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
),
|
||||
child: SafeArea(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
ModalTopBar(
|
||||
title: S.of(context).passphrase_entry,
|
||||
leadingIcon: const Icon(Icons.close),
|
||||
leadingSemanticLabel: S.of(context).close,
|
||||
onLeadingPressed: Navigator.of(context).pop,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 18),
|
||||
child: Column(
|
||||
spacing: 12,
|
||||
children: [
|
||||
BaseTextFormField(
|
||||
controller: controller,
|
||||
hintText: S.of(context).passphrase_raw,
|
||||
autocorrect: false,
|
||||
enableSuggestions: false,
|
||||
),
|
||||
const SizedBox(),
|
||||
NewPrimaryButton(
|
||||
onPressed: Navigator.of(context).pop,
|
||||
text: S.of(context).restore_next,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
textColor: Theme.of(context).colorScheme.onPrimary,
|
||||
),
|
||||
const SizedBox(),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -40,13 +40,11 @@ class EditTokenPage extends BasePage {
|
||||
: S.current.add_token;
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) {
|
||||
return EditTokenPageBody(
|
||||
Widget body(BuildContext context) => EditTokenPageBody(
|
||||
homeSettingsViewModel: homeSettingsViewModel,
|
||||
token: token,
|
||||
initialContractAddress: initialContractAddress,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class EditTokenPageBody extends StatefulWidget {
|
||||
@@ -83,6 +81,11 @@ class _EditTokenPageBodyState extends State<EditTokenPageBody> {
|
||||
bool _disclaimerChecked = false;
|
||||
bool isEditingToken = false;
|
||||
bool _isTokenVerified = false;
|
||||
bool _isJupiterVerified = false;
|
||||
String? _jupiterVerifiedAddress;
|
||||
|
||||
bool get _isJupiterVerifiedForCurrentAddress =>
|
||||
_isJupiterVerified && _jupiterVerifiedAddress == _contractAddressController.text;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@@ -129,14 +132,28 @@ class _EditTokenPageBodyState extends State<EditTokenPageBody> {
|
||||
final isVerified = widget.homeSettingsViewModel.checkIfTokenIsWhitelisted(contractAddress);
|
||||
|
||||
if (!mounted) return;
|
||||
|
||||
setState(() {
|
||||
_isTokenVerified = isVerified;
|
||||
});
|
||||
|
||||
_checkIfTokenIsVerifiedOnJupiter(contractAddress);
|
||||
}
|
||||
|
||||
Future<void> _checkIfTokenIsVerifiedOnJupiter(String contractAddress) async {
|
||||
final isVerified =
|
||||
await widget.homeSettingsViewModel.checkIfTokenIsVerifiedOnJupiter(contractAddress);
|
||||
|
||||
if (!mounted || _contractAddressController.text != contractAddress) return;
|
||||
|
||||
setState(() {
|
||||
_isJupiterVerified = isVerified ?? false;
|
||||
_jupiterVerifiedAddress = contractAddress;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
Widget build(BuildContext context) => GestureDetector(
|
||||
onTap: () => FocusScope.of(context).unfocus(),
|
||||
child: ScrollableWithBottomSection(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
@@ -174,8 +191,7 @@ class _EditTokenPageBodyState extends State<EditTokenPageBody> {
|
||||
SizedBox(height: 20),
|
||||
],
|
||||
Observer(
|
||||
builder: (context) {
|
||||
return Row(
|
||||
builder: (context) => Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: LoadingPrimaryButton(
|
||||
@@ -210,14 +226,12 @@ class _EditTokenPageBodyState extends State<EditTokenPageBody> {
|
||||
if (isTokenAlreadyAdded) {
|
||||
showPopUp<void>(
|
||||
context: context,
|
||||
builder: (dialogContext) {
|
||||
return AlertWithOneAction(
|
||||
builder: (dialogContext) => AlertWithOneAction(
|
||||
alertTitle: S.current.warning,
|
||||
alertContent: S.of(context).token_already_exists,
|
||||
buttonText: S.of(context).ok,
|
||||
buttonAction: () => Navigator.of(dialogContext).pop(),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -225,13 +239,15 @@ class _EditTokenPageBodyState extends State<EditTokenPageBody> {
|
||||
final isWhitelisted = await widget.homeSettingsViewModel
|
||||
.checkIfTokenIsWhitelisted(_contractAddressController.text);
|
||||
|
||||
final hasPotentialError = !isWhitelisted &&
|
||||
final isTrusted = isWhitelisted || _isJupiterVerifiedForCurrentAddress;
|
||||
|
||||
final hasPotentialError = !isTrusted &&
|
||||
await widget.homeSettingsViewModel
|
||||
.checkIfERC20TokenContractAddressIsAPotentialScamAddress(
|
||||
_contractAddressController.text,
|
||||
);
|
||||
|
||||
bool isPotentialScam = hasPotentialError && !isWhitelisted;
|
||||
bool isPotentialScam = hasPotentialError && !isTrusted;
|
||||
|
||||
// Normalize to catch homoglyph spoofing attacks
|
||||
final tokenSymbol = normalizeHomoglyphs(
|
||||
@@ -243,7 +259,7 @@ class _EditTokenPageBodyState extends State<EditTokenPageBody> {
|
||||
// (e.g. fake ETH on Ethereum, fake SOL on Solana)
|
||||
final nativeSymbol =
|
||||
widget.homeSettingsViewModel.nativeToken.title.toUpperCase();
|
||||
if (tokenSymbol == nativeSymbol && !isWhitelisted) {
|
||||
if (tokenSymbol == nativeSymbol && !isTrusted) {
|
||||
isPotentialScam = true;
|
||||
}
|
||||
|
||||
@@ -252,7 +268,7 @@ class _EditTokenPageBodyState extends State<EditTokenPageBody> {
|
||||
if (widget.homeSettingsViewModel.checkIfTokenSymbolMatchesDefaultToken(
|
||||
tokenSymbol,
|
||||
) &&
|
||||
!isWhitelisted) {
|
||||
!isTrusted) {
|
||||
isPotentialScam = true;
|
||||
}
|
||||
|
||||
@@ -277,23 +293,20 @@ class _EditTokenPageBodyState extends State<EditTokenPageBody> {
|
||||
} catch (e) {
|
||||
showPopUp<void>(
|
||||
context: context,
|
||||
builder: (dialogContext) {
|
||||
return AlertWithOneAction(
|
||||
builder: (dialogContext) => AlertWithOneAction(
|
||||
alertTitle: S.current.warning,
|
||||
alertContent: e.toString(),
|
||||
buttonText: S.of(context).ok,
|
||||
buttonAction: () => Navigator.of(dialogContext).pop(),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
if (hasPotentialError && !isWhitelisted) {
|
||||
if (hasPotentialError && !isTrusted) {
|
||||
showPopUp<void>(
|
||||
context: context,
|
||||
builder: (dialogContext) {
|
||||
return AlertWithTwoActions(
|
||||
builder: (dialogContext) => AlertWithTwoActions(
|
||||
alertTitle: S.current.warning,
|
||||
alertContent: S.current.contract_warning,
|
||||
rightButtonText: S.of(context).continue_text,
|
||||
@@ -303,8 +316,7 @@ class _EditTokenPageBodyState extends State<EditTokenPageBody> {
|
||||
await actionCall();
|
||||
},
|
||||
actionLeftButton: () => Navigator.of(dialogContext).pop(),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
} else {
|
||||
try {
|
||||
@@ -312,14 +324,12 @@ class _EditTokenPageBodyState extends State<EditTokenPageBody> {
|
||||
} catch (e) {
|
||||
showPopUp<void>(
|
||||
context: context,
|
||||
builder: (dialogContext) {
|
||||
return AlertWithOneAction(
|
||||
builder: (dialogContext) => AlertWithOneAction(
|
||||
alertTitle: "Unable to add token",
|
||||
alertContent: "$e",
|
||||
buttonText: S.of(context).ok,
|
||||
buttonAction: () => Navigator.of(context).pop(),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
if (mounted) {
|
||||
@@ -334,14 +344,12 @@ class _EditTokenPageBodyState extends State<EditTokenPageBody> {
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _getTokenInfo() async {
|
||||
if (_contractAddressController.text.isNotEmpty) {
|
||||
@@ -377,8 +385,7 @@ class _EditTokenPageBodyState extends State<EditTokenPageBody> {
|
||||
}
|
||||
}
|
||||
|
||||
Widget _tokenForm() {
|
||||
return Form(
|
||||
Widget _tokenForm() => Form(
|
||||
key: _formKey,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
@@ -402,7 +409,7 @@ class _EditTokenPageBodyState extends State<EditTokenPageBody> {
|
||||
size: 75,
|
||||
),
|
||||
),
|
||||
if (_isTokenVerified)
|
||||
if (_isTokenVerified || _isJupiterVerifiedForCurrentAddress)
|
||||
Positioned(
|
||||
bottom: 0,
|
||||
right: 0,
|
||||
@@ -425,6 +432,16 @@ class _EditTokenPageBodyState extends State<EditTokenPageBody> {
|
||||
),
|
||||
],
|
||||
),
|
||||
if (_isJupiterVerifiedForCurrentAddress) ...[
|
||||
const SizedBox(height: 12),
|
||||
Text(
|
||||
S.of(context).token_verified_on_jupiter,
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
),
|
||||
],
|
||||
SizedBox(height: 25),
|
||||
AddressTextField(
|
||||
controller: _contractAddressController,
|
||||
@@ -517,5 +534,4 @@ class _EditTokenPageBodyState extends State<EditTokenPageBody> {
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ class FavoriteTokenModal extends StatelessWidget {
|
||||
ModalTopBar(
|
||||
title: S.of(context).favorite_token,
|
||||
leadingIcon: Icon(Icons.close),
|
||||
leadingSemanticLabel: S.of(context).close,
|
||||
onLeadingPressed: Navigator.of(context).pop,
|
||||
),
|
||||
Expanded(
|
||||
|
||||
@@ -334,37 +334,6 @@ class CryptoBalanceWidget extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
],
|
||||
if (dashboardViewModel.showZcashMissingFundsCard) ...[
|
||||
SizedBox(height: 10),
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(16, 0, 16, 8),
|
||||
child: InfoCard(
|
||||
title: S.of(context).zcash_card_missing_funds,
|
||||
description: S.of(context).zcash_card_description,
|
||||
leftButtonTitle: S.of(context).zcash_card_dismiss,
|
||||
rightButtonTitle: S.of(context).zcash_card_scan,
|
||||
image: 'assets/new-ui/crypto_full_icons/zcash.svg',
|
||||
leftButtonAction: () => _dismissZcash(context),
|
||||
rightButtonAction: () => _rescanInternalChangeZcash(context),
|
||||
hintWidget: GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTap: () => launchUrl(
|
||||
Uri.parse(
|
||||
"https://docs.cakewallet.com/tutorials/zashi/#creating-a-new-zcash-wallet-on-cake"),
|
||||
mode: LaunchMode.externalApplication,
|
||||
),
|
||||
child: Text(
|
||||
S.of(context).learn_more,
|
||||
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
height: 1,
|
||||
),
|
||||
softWrap: true,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
if (dashboardViewModel.showDecredInfoCard) ...[
|
||||
SizedBox(height: 10),
|
||||
Padding(
|
||||
@@ -494,21 +463,6 @@ class CryptoBalanceWidget extends StatelessWidget {
|
||||
dashboardViewModel.setMwebEnabled();
|
||||
}
|
||||
|
||||
Future<void> _rescanInternalChangeZcash(BuildContext context) async {
|
||||
dashboardViewModel.rescanInternalChangeZcash();
|
||||
await showPopUp<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) => AlertWithOneAction(
|
||||
alertTitle: S.of(context).alert_notice,
|
||||
alertContent: S.of(context).zcash_card_warning,
|
||||
buttonText: S.of(context).understand,
|
||||
buttonAction: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _dismissMweb(BuildContext context) async {
|
||||
await showPopUp<void>(
|
||||
context: context,
|
||||
@@ -522,18 +476,4 @@ class CryptoBalanceWidget extends StatelessWidget {
|
||||
));
|
||||
dashboardViewModel.dismissMweb();
|
||||
}
|
||||
|
||||
Future<void> _dismissZcash(BuildContext context) async {
|
||||
await showPopUp<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) => AlertWithOneAction(
|
||||
alertTitle: S.of(context).alert_notice,
|
||||
alertContent: S.of(context).zcash_card_enable_later,
|
||||
buttonText: S.of(context).understand,
|
||||
buttonAction: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
));
|
||||
dashboardViewModel.dismissZcash();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,7 +111,7 @@ class TransactionsPage extends StatelessWidget {
|
||||
key: item.key,
|
||||
onTap: () => Navigator.of(context)
|
||||
.pushNamed(Routes.transactionDetails, arguments: transaction),
|
||||
isShield: transaction.additionalInfo['autoShield'] == true,
|
||||
isShield: transaction.additionalInfo['isAutoShield'] == true,
|
||||
direction: transaction.direction,
|
||||
formattedDate: DateFormat('HH:mm').format(transaction.date),
|
||||
formattedAmount: item.formattedCryptoAmount,
|
||||
|
||||
@@ -59,6 +59,7 @@ class _SignPageState extends State<SignPage> {
|
||||
ModalTopBar(
|
||||
title: S.current.sign_verify_title,
|
||||
leadingIcon: Icon(Icons.arrow_back_ios_new),
|
||||
leadingSemanticLabel: S.current.seed_alert_back,
|
||||
onLeadingPressed: Navigator.of(context).pop,
|
||||
),
|
||||
Expanded(
|
||||
|
||||
@@ -187,50 +187,56 @@ class _NEWNewMainNavBarState extends State<NewMainNavBar> {
|
||||
? iconHorizontalPadding / 100
|
||||
: 0),
|
||||
curve: Curves.easeOutCubic,
|
||||
child: InkWell(
|
||||
splashFactory: NoSplash.splashFactory,
|
||||
splashColor: Colors.transparent,
|
||||
borderRadius: BorderRadius.circular(pillBorderRadius),
|
||||
onTap: () => _onItemTap(i),
|
||||
child: AnimatedContainer(
|
||||
duration:
|
||||
_firstFrame ? Duration.zero : inactiveIconMoveDuration,
|
||||
curve: Curves.easeOutCubic,
|
||||
width: i == widget.selectedIndex ? pillWidth : iconBoxWidth,
|
||||
alignment: Alignment.center,
|
||||
child: AnimatedAlign(
|
||||
duration: inactiveIconFadeDuration,
|
||||
child: Semantics(
|
||||
button: true,
|
||||
selected: i == widget.selectedIndex,
|
||||
inMutuallyExclusiveGroup: true,
|
||||
label: visibleActions[i].name(context),
|
||||
child: InkWell(
|
||||
splashFactory: NoSplash.splashFactory,
|
||||
splashColor: Colors.transparent,
|
||||
borderRadius: BorderRadius.circular(pillBorderRadius),
|
||||
onTap: () => _onItemTap(i),
|
||||
child: AnimatedContainer(
|
||||
duration:
|
||||
_firstFrame ? Duration.zero : inactiveIconMoveDuration,
|
||||
curve: Curves.easeOutCubic,
|
||||
width: i == widget.selectedIndex ? pillWidth : iconBoxWidth,
|
||||
alignment: Alignment.center,
|
||||
child: AnimatedScale(
|
||||
duration: inactiveIconAppearDuration,
|
||||
child: AnimatedAlign(
|
||||
duration: inactiveIconFadeDuration,
|
||||
curve: Curves.easeOutCubic,
|
||||
scale: (i == widget.selectedIndex) ? 0.857 : 1.0,
|
||||
child: TweenAnimationBuilder<Color?>(
|
||||
tween: ColorTween(
|
||||
begin: (i == widget.selectedIndex)
|
||||
? inactiveColor
|
||||
: activeColor,
|
||||
end: (i == widget.selectedIndex)
|
||||
? activeColor
|
||||
: inactiveColor,
|
||||
),
|
||||
duration: iconColorChangeDuration,
|
||||
builder: (context, value, child) {
|
||||
return Container(
|
||||
height: NewMainNavBar.barHeight,
|
||||
child: CakeImageWidget(
|
||||
imageUrl: visibleActions[i].image,
|
||||
width: iconWidth,
|
||||
height: iconHeight,
|
||||
//fit: BoxFit.scaleDown,
|
||||
colorFilter: ColorFilter.mode(
|
||||
value ?? inactiveColor,
|
||||
BlendMode.srcIn,
|
||||
alignment: Alignment.center,
|
||||
child: AnimatedScale(
|
||||
duration: inactiveIconAppearDuration,
|
||||
curve: Curves.easeOutCubic,
|
||||
scale: (i == widget.selectedIndex) ? 0.857 : 1.0,
|
||||
child: TweenAnimationBuilder<Color?>(
|
||||
tween: ColorTween(
|
||||
begin: (i == widget.selectedIndex)
|
||||
? inactiveColor
|
||||
: activeColor,
|
||||
end: (i == widget.selectedIndex)
|
||||
? activeColor
|
||||
: inactiveColor,
|
||||
),
|
||||
duration: iconColorChangeDuration,
|
||||
builder: (context, value, child) {
|
||||
return Container(
|
||||
height: NewMainNavBar.barHeight,
|
||||
child: CakeImageWidget(
|
||||
imageUrl: visibleActions[i].image,
|
||||
width: iconWidth,
|
||||
height: iconHeight,
|
||||
//fit: BoxFit.scaleDown,
|
||||
colorFilter: ColorFilter.mode(
|
||||
value ?? inactiveColor,
|
||||
BlendMode.srcIn,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
);
|
||||
}),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -306,11 +312,15 @@ class AnimatedPill extends StatelessWidget {
|
||||
children: [
|
||||
Padding(
|
||||
padding: EdgeInsets.only(left: pillIconWidth + 2),
|
||||
child: Text(
|
||||
currentAction.name(context),
|
||||
style: pillTextStyle.copyWith(color: contentColor),
|
||||
overflow: TextOverflow.fade,
|
||||
softWrap: false,
|
||||
// The selected tab's InkWell already announces this name, so the
|
||||
// pill text must not become a second stop for screen readers.
|
||||
child: ExcludeSemantics(
|
||||
child: Text(
|
||||
currentAction.name(context),
|
||||
style: pillTextStyle.copyWith(color: contentColor),
|
||||
overflow: TextOverflow.fade,
|
||||
softWrap: false,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -659,8 +659,8 @@ class ExchangePage extends BasePage {
|
||||
final parsedAddresses = await adrResService.resolve(
|
||||
query: domain, wallet: exchangeViewModel.wallet, currency: currency);
|
||||
return parsedAddresses.isNotEmpty
|
||||
? parsedAddresses.first.parsedAddressByCurrencyMap[currency] ?? ''
|
||||
: '';
|
||||
? (parsedAddresses.first.parsedAddressByCurrencyMap[currency] ?? domain)
|
||||
: domain;
|
||||
}
|
||||
|
||||
void _showFeeAlert(BuildContext context) async {
|
||||
|
||||
@@ -14,11 +14,13 @@ import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/src/widgets/scrollable_with_bottom_section.dart';
|
||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||
import 'package:cake_wallet/utils/feature_flag.dart';
|
||||
import 'package:cake_wallet/view_model/advanced_privacy_settings_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/node_list/node_create_or_edit_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/seed_settings_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/settings/choices_list_item.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:cake_wallet/zcash/zcash_network_type.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
|
||||
@@ -28,6 +30,8 @@ class AdvancedPrivacySettingsPage extends BasePage {
|
||||
required this.isChildWallet,
|
||||
required this.useTestnet,
|
||||
required this.toggleUseTestnet,
|
||||
required this.zcashNetwork,
|
||||
required this.setZcashNetwork,
|
||||
required this.advancedPrivacySettingsViewModel,
|
||||
required this.nodeViewModel,
|
||||
required this.seedSettingsViewModel,
|
||||
@@ -44,6 +48,8 @@ class AdvancedPrivacySettingsPage extends BasePage {
|
||||
final bool isChildWallet;
|
||||
final bool useTestnet;
|
||||
final Function(bool? val) toggleUseTestnet;
|
||||
final int zcashNetwork;
|
||||
final void Function(int network) setZcashNetwork;
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) => _AdvancedPrivacySettingsBody(
|
||||
@@ -51,6 +57,8 @@ class AdvancedPrivacySettingsPage extends BasePage {
|
||||
isChildWallet,
|
||||
useTestnet,
|
||||
toggleUseTestnet,
|
||||
zcashNetwork,
|
||||
setZcashNetwork,
|
||||
advancedPrivacySettingsViewModel,
|
||||
nodeViewModel,
|
||||
seedSettingsViewModel,
|
||||
@@ -63,6 +71,8 @@ class _AdvancedPrivacySettingsBody extends StatefulWidget {
|
||||
this.isChildWallet,
|
||||
this.useTestnet,
|
||||
this.toggleUseTestnet,
|
||||
this.zcashNetwork,
|
||||
this.setZcashNetwork,
|
||||
this.privacySettingsViewModel,
|
||||
this.nodeViewModel,
|
||||
this.seedTypeViewModel,
|
||||
@@ -76,6 +86,8 @@ class _AdvancedPrivacySettingsBody extends StatefulWidget {
|
||||
final bool isChildWallet;
|
||||
final bool useTestnet;
|
||||
final Function(bool? val) toggleUseTestnet;
|
||||
final int zcashNetwork;
|
||||
final void Function(int network) setZcashNetwork;
|
||||
|
||||
@override
|
||||
_AdvancedPrivacySettingsBodyState createState() => _AdvancedPrivacySettingsBodyState();
|
||||
@@ -87,6 +99,7 @@ class _AdvancedPrivacySettingsBodyState extends State<_AdvancedPrivacySettingsBo
|
||||
final _formKey = GlobalKey<NodeFormState>();
|
||||
final _passphraseFormKey = GlobalKey<FormState>();
|
||||
bool? testnetValue;
|
||||
int? zcashNetworkValue;
|
||||
|
||||
bool obscurePassphrase = true;
|
||||
|
||||
@@ -112,6 +125,7 @@ class _AdvancedPrivacySettingsBodyState extends State<_AdvancedPrivacySettingsBo
|
||||
if (testnetValue == null && widget.useTestnet) {
|
||||
testnetValue = widget.useTestnet;
|
||||
}
|
||||
zcashNetworkValue ??= widget.zcashNetwork;
|
||||
|
||||
return Container(
|
||||
padding: EdgeInsets.only(top: 24),
|
||||
@@ -281,6 +295,20 @@ class _AdvancedPrivacySettingsBodyState extends State<_AdvancedPrivacySettingsBo
|
||||
],
|
||||
);
|
||||
}),
|
||||
if (FeatureFlag.hasDevOptions &&
|
||||
widget.privacySettingsViewModel.type == WalletType.zcash)
|
||||
SettingsChoicesCell(
|
||||
ChoicesListItem<int>(
|
||||
title: 'Zcash network',
|
||||
items: ZcashNetworkType.values,
|
||||
selectedItem: zcashNetworkValue ?? ZcashNetworkType.mainnet,
|
||||
displayItem: ZcashNetworkType.label,
|
||||
onItemSelected: (final network) {
|
||||
setState(() => zcashNetworkValue = network);
|
||||
widget.setZcashNetwork(network);
|
||||
},
|
||||
),
|
||||
),
|
||||
if (widget.privacySettingsViewModel.type == WalletType.bitcoin ||
|
||||
widget.privacySettingsViewModel.type == WalletType.decred)
|
||||
Builder(builder: (_) {
|
||||
@@ -332,11 +360,9 @@ class _AdvancedPrivacySettingsBodyState extends State<_AdvancedPrivacySettingsBo
|
||||
|
||||
widget.nodeViewModel.save();
|
||||
}
|
||||
if (passphraseController.text.isNotEmpty) {
|
||||
if (_passphraseFormKey.currentState != null &&
|
||||
!_passphraseFormKey.currentState!.validate()) {
|
||||
return;
|
||||
}
|
||||
if (_passphraseFormKey.currentState != null &&
|
||||
!_passphraseFormKey.currentState!.validate()) {
|
||||
return;
|
||||
}
|
||||
|
||||
widget.seedTypeViewModel.setPassphrase(passphraseController.text);
|
||||
|
||||
@@ -316,6 +316,8 @@ class _WalletNameFormState extends State<WalletNameForm> {
|
||||
"type": _walletNewVM.type,
|
||||
"useTestnet": _walletNewVM.useTestnet,
|
||||
"toggleTestnet": _walletNewVM.toggleUseTestnet,
|
||||
"zcashNetwork": _walletNewVM.zcashNetwork,
|
||||
"setZcashNetwork": _walletNewVM.setZcashNetwork,
|
||||
"isChildWallet": widget.isChildWallet,
|
||||
});
|
||||
},
|
||||
|
||||
@@ -93,8 +93,10 @@ class _NodeCreateOrEditPageState extends State<NodeCreateOrEditPage> {
|
||||
ModalTopBar(
|
||||
title: widget.editingNode != null ? S.current.edit_node : S.current.node_new,
|
||||
leadingIcon: Icon(Icons.arrow_back_ios_new),
|
||||
leadingSemanticLabel: S.current.seed_alert_back,
|
||||
onLeadingPressed: Navigator.of(context).pop,
|
||||
trailingIcon: Icon(Icons.check),
|
||||
trailingSemanticLabel: S.current.save,
|
||||
onTrailingPressed: () async {
|
||||
if (_nodeFormKey.currentState != null && !_nodeFormKey.currentState!.validate()) {
|
||||
return;
|
||||
|
||||
@@ -24,6 +24,7 @@ class NodeSharePage extends StatelessWidget {
|
||||
ModalTopBar(
|
||||
title: S.of(context).share,
|
||||
leadingIcon: Icon(Icons.arrow_back_ios_new),
|
||||
leadingSemanticLabel: S.of(context).seed_alert_back,
|
||||
onLeadingPressed: Navigator.of(context).pop,
|
||||
),
|
||||
Expanded(
|
||||
|
||||
@@ -75,6 +75,7 @@ class _PowNodeCreateOrEditPageState extends State<PowNodeCreateOrEditPage> {
|
||||
ModalTopBar(
|
||||
title: widget.editingNode != null ? S.current.edit_node : S.current.node_new,
|
||||
leadingIcon: Icon(Icons.arrow_back_ios_new),
|
||||
leadingSemanticLabel: S.current.seed_alert_back,
|
||||
onLeadingPressed: Navigator.of(context).pop,
|
||||
trailingIcon: CakeImageWidget(
|
||||
imageUrl: "assets/new-ui/scan.svg",
|
||||
@@ -83,6 +84,7 @@ class _PowNodeCreateOrEditPageState extends State<PowNodeCreateOrEditPage> {
|
||||
colorFilter:
|
||||
ColorFilter.mode(Theme.of(context).colorScheme.primary, BlendMode.srcIn),
|
||||
),
|
||||
trailingSemanticLabel: S.current.scan,
|
||||
onTrailingPressed: () =>
|
||||
widget.nodeCreateOrEditViewModel.scanQRCodeForNewNode(context),
|
||||
),
|
||||
|
||||
@@ -22,6 +22,7 @@ class AddressListPage extends StatelessWidget {
|
||||
ModalTopBar(
|
||||
title: S.of(context).accounts_subaddresses,
|
||||
leadingIcon: Icon(Icons.arrow_back_ios_new),
|
||||
leadingSemanticLabel: S.of(context).seed_alert_back,
|
||||
onLeadingPressed: Navigator.of(context).pop,
|
||||
),
|
||||
Expanded(
|
||||
|
||||
@@ -114,6 +114,7 @@ class _RescanPageState extends State<RescanPage> {
|
||||
? S.current.silent_payments_scanning
|
||||
: S.current.rescan,
|
||||
leadingIcon: Icon(Icons.arrow_back_ios_new),
|
||||
leadingSemanticLabel: S.current.seed_alert_back,
|
||||
onLeadingPressed: Navigator.of(context).pop),
|
||||
Expanded(
|
||||
child: Padding(
|
||||
|
||||
@@ -99,7 +99,7 @@ class WalletRestorePage extends BasePage {
|
||||
children: [
|
||||
Observer(
|
||||
builder: (context) {
|
||||
return walletRestoreViewModel.mode == WalletRestoreMode.seed
|
||||
return walletRestoreViewModel.passphraseAvailable
|
||||
? StandardCheckbox(
|
||||
captionColor: Theme.of(context).colorScheme.onSecondaryContainer,
|
||||
value: walletRestoreViewModel.hasPassphrase,
|
||||
@@ -121,7 +121,9 @@ class WalletRestorePage extends BasePage {
|
||||
'isFromRestore': true,
|
||||
'type': walletRestoreViewModel.type,
|
||||
'useTestnet': walletRestoreViewModel.useTestnet,
|
||||
'toggleTestnet': walletRestoreViewModel.toggleUseTestnet
|
||||
'toggleTestnet': walletRestoreViewModel.toggleUseTestnet,
|
||||
'zcashNetwork': walletRestoreViewModel.zcashNetwork,
|
||||
'setZcashNetwork': walletRestoreViewModel.setZcashNetwork,
|
||||
},
|
||||
);
|
||||
},
|
||||
|
||||
@@ -34,6 +34,7 @@ class ConnectionSyncPage extends BasePage {
|
||||
topBar: ModalTopBar(
|
||||
title: "",
|
||||
leadingIcon: Icon(Icons.arrow_back_ios_new),
|
||||
leadingSemanticLabel: S.of(context).seed_alert_back,
|
||||
onLeadingPressed: () => Navigator.of(context).pop()),
|
||||
header: ModalHeader(
|
||||
iconPath: "assets/new-ui/settings_row_icons/connections.svg",
|
||||
|
||||
@@ -35,6 +35,7 @@ class DisplaySettingsPage extends StatelessWidget {
|
||||
topBar: ModalTopBar(
|
||||
title: S.of(context).display,
|
||||
leadingIcon: Icon(Icons.arrow_back_ios_new),
|
||||
leadingSemanticLabel: S.of(context).seed_alert_back,
|
||||
onLeadingPressed: () => Navigator.of(context).pop(),
|
||||
),
|
||||
content: Column(
|
||||
@@ -115,21 +116,12 @@ class DisplaySettingsPage extends StatelessWidget {
|
||||
onChanged: (val) {
|
||||
_displaySettingsViewModel.setDisableTradeOption(val);
|
||||
}),
|
||||
if (_displaySettingsViewModel.showZcashCardSetting)
|
||||
ListItemToggle(
|
||||
keyValue: "display_settings_show_zcashcard",
|
||||
label: S.of(context).show_zcash_card,
|
||||
value: _displaySettingsViewModel.showZcashCard,
|
||||
onChanged: (val) {
|
||||
_displaySettingsViewModel.setShowZcashCard(val);
|
||||
}),
|
||||
ListItemSelector(
|
||||
keyValue: "display_settings_sync_status_display",
|
||||
label: S.of(context).sync_status_display_mode,
|
||||
options: [_displaySettingsViewModel.syncStatusDisplayMode.title],
|
||||
onTap: () async {
|
||||
final items = SyncStatusDisplayMode.values.toList();
|
||||
|
||||
final selectedAtIndex =
|
||||
items.indexOf(_displaySettingsViewModel.syncStatusDisplayMode);
|
||||
|
||||
|
||||
@@ -40,6 +40,7 @@ class _ManageNodesPageState extends State<ManageNodesPage> {
|
||||
topBar: ModalTopBar(
|
||||
title: S.of(context).manage_nodes,
|
||||
leadingIcon: Icon(Icons.arrow_back_ios_new),
|
||||
leadingSemanticLabel: S.of(context).seed_alert_back,
|
||||
onLeadingPressed: () => Navigator.of(context).pop(),
|
||||
trailingWidget: Row(
|
||||
spacing: 8,
|
||||
@@ -50,11 +51,13 @@ class _ManageNodesPageState extends State<ManageNodesPage> {
|
||||
icon: widget.nodeListViewModel.isTestingNodeSpeed
|
||||
? CupertinoActivityIndicator()
|
||||
: Icon(Icons.refresh),
|
||||
semanticLabel: S.of(context).test_node_speeds,
|
||||
onPressed: () => widget.nodeListViewModel.speedTestNodes()),
|
||||
),
|
||||
ModernButton(
|
||||
size: 36,
|
||||
icon: Icon(Icons.add),
|
||||
semanticLabel: S.of(context).add_new_node,
|
||||
onPressed: () async {
|
||||
final res = await Navigator.of(context)
|
||||
.pushNamed(widget.isPow ? Routes.newPowNode : Routes.newNode);
|
||||
|
||||
@@ -34,6 +34,7 @@ class _MwebNodePageState extends State<MwebNodePage> {
|
||||
topBar: ModalTopBar(
|
||||
title: S.current.litecoin_mweb_settings,
|
||||
onLeadingPressed: Navigator.of(context).pop,
|
||||
leadingSemanticLabel: S.current.seed_alert_back,
|
||||
leadingIcon: Icon(Icons.arrow_back_ios_new)),
|
||||
content: Container(
|
||||
child: NewListSections(controllers: {
|
||||
|
||||
@@ -42,6 +42,7 @@ class OtherSettingsPage extends BasePage {
|
||||
topBar: ModalTopBar(
|
||||
title: S.of(context).other,
|
||||
leadingIcon: Icon(Icons.arrow_back_ios_new),
|
||||
leadingSemanticLabel: S.of(context).seed_alert_back,
|
||||
onLeadingPressed: () => Navigator.of(context).pop(),
|
||||
),
|
||||
// header: ModalHeader(
|
||||
|
||||
@@ -26,6 +26,7 @@ class PrivacyPage extends BasePage {
|
||||
topBar: ModalTopBar(
|
||||
title: "",
|
||||
leadingIcon: Icon(Icons.arrow_back_ios_new),
|
||||
leadingSemanticLabel: S.of(context).seed_alert_back,
|
||||
onLeadingPressed: () => Navigator.of(context).pop(),
|
||||
),
|
||||
header: ModalHeader(
|
||||
|
||||
@@ -45,6 +45,7 @@ class SecurityBackupPage extends BasePage {
|
||||
topBar: ModalTopBar(
|
||||
title: "",
|
||||
leadingIcon: Icon(Icons.arrow_back_ios_new),
|
||||
leadingSemanticLabel: S.of(context).seed_alert_back,
|
||||
onLeadingPressed: () => Navigator.of(context).pop()),
|
||||
header: ModalHeader(
|
||||
iconPath: "assets/new-ui/settings_row_icons/security.svg",
|
||||
|
||||
@@ -22,6 +22,7 @@ class SilentPaymentsSettingsPage extends StatelessWidget {
|
||||
ModalTopBar(
|
||||
title: S.current.silent_payments_settings,
|
||||
leadingIcon: Icon(Icons.arrow_back_ios_new),
|
||||
leadingSemanticLabel: S.current.seed_alert_back,
|
||||
onLeadingPressed: Navigator.of(context).pop,
|
||||
),
|
||||
Expanded(
|
||||
|
||||
@@ -144,6 +144,7 @@ class _TOTPEnterCodeState extends State<TOTPEnterCode> {
|
||||
ModalTopBar(
|
||||
title: widget.isForSetup ? S.current.setup_2fa : S.current.verify_with_2fa,
|
||||
leadingIcon: Icon(Icons.arrow_back_ios_new),
|
||||
leadingSemanticLabel: S.current.seed_alert_back,
|
||||
onLeadingPressed: Navigator.of(context).pop,
|
||||
),
|
||||
Expanded(
|
||||
|
||||
@@ -22,6 +22,7 @@ class SupportChatPage extends StatelessWidget {
|
||||
ModalTopBar(
|
||||
title: S.current.settings_support,
|
||||
leadingIcon: Icon(Icons.arrow_back_ios_new),
|
||||
leadingSemanticLabel: S.current.seed_alert_back,
|
||||
onLeadingPressed: Navigator.of(context).pop),
|
||||
FutureBuilder<String>(
|
||||
future: getCookie(),
|
||||
|
||||
@@ -1,34 +1,36 @@
|
||||
import 'package:cake_wallet/core/execution_state.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/src/screens/transaction_details/rbf_details_list_fee_picker_item.dart';
|
||||
import 'package:cake_wallet/src/screens/transaction_details/standart_list_item.dart';
|
||||
import 'package:cake_wallet/src/screens/transaction_details/textfield_list_item.dart';
|
||||
import 'package:cake_wallet/src/screens/transaction_details/transaction_expandable_list_item.dart';
|
||||
import 'package:cake_wallet/src/screens/transaction_details/widgets/textfield_list_row.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
|
||||
import 'package:cake_wallet/src/widgets/bottom_sheet/base_bottom_sheet_widget.dart';
|
||||
import 'package:cake_wallet/src/widgets/bottom_sheet/confirm_sending_bottom_sheet_widget.dart';
|
||||
import 'package:cake_wallet/src/widgets/bottom_sheet/info_bottom_sheet_widget.dart';
|
||||
import 'package:cake_wallet/src/widgets/list_row.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/src/widgets/standard_expandable_list.dart';
|
||||
import 'package:cake_wallet/src/widgets/standard_list.dart';
|
||||
import 'package:cake_wallet/src/widgets/standard_picker_list.dart';
|
||||
import 'package:cake_wallet/utils/show_bar.dart';
|
||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||
import 'package:cake_wallet/view_model/send/send_view_model_state.dart';
|
||||
import 'package:cake_wallet/view_model/transaction_details_view_model.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import "package:cake_wallet/core/execution_state.dart";
|
||||
import "package:cake_wallet/generated/i18n.dart";
|
||||
import "package:cake_wallet/src/screens/base_page.dart";
|
||||
import "package:cake_wallet/src/screens/transaction_details/rbf_details_list_fee_picker_item.dart";
|
||||
import "package:cake_wallet/src/screens/transaction_details/standart_list_item.dart";
|
||||
import "package:cake_wallet/src/screens/transaction_details/textfield_list_item.dart";
|
||||
import "package:cake_wallet/src/screens/transaction_details/transaction_expandable_list_item.dart";
|
||||
import "package:cake_wallet/src/screens/transaction_details/widgets/textfield_list_row.dart";
|
||||
import "package:cake_wallet/src/widgets/alert_with_one_action.dart";
|
||||
import "package:cake_wallet/src/widgets/alert_with_two_actions.dart";
|
||||
import "package:cake_wallet/src/widgets/bottom_sheet/base_bottom_sheet_widget.dart";
|
||||
import "package:cake_wallet/src/widgets/bottom_sheet/confirm_sending_bottom_sheet_widget.dart";
|
||||
import "package:cake_wallet/src/widgets/bottom_sheet/info_bottom_sheet_widget.dart";
|
||||
import "package:cake_wallet/src/widgets/list_row.dart";
|
||||
import "package:cake_wallet/src/widgets/primary_button.dart";
|
||||
import "package:cake_wallet/src/widgets/standard_expandable_list.dart";
|
||||
import "package:cake_wallet/src/widgets/standard_list.dart";
|
||||
import "package:cake_wallet/src/widgets/standard_picker_list.dart";
|
||||
import "package:cake_wallet/utils/show_bar.dart";
|
||||
import "package:cake_wallet/utils/show_pop_up.dart";
|
||||
import "package:cake_wallet/view_model/send/send_view_model_state.dart";
|
||||
import "package:cake_wallet/view_model/transaction_details_view_model.dart";
|
||||
import "package:flutter/material.dart";
|
||||
import "package:flutter/services.dart";
|
||||
import "package:flutter_mobx/flutter_mobx.dart";
|
||||
import "package:mobx/mobx.dart";
|
||||
|
||||
class RBFDetailsPage extends BasePage {
|
||||
RBFDetailsPage({required this.transactionDetailsViewModel, required this.rawTransaction}) {
|
||||
transactionDetailsViewModel.addBumpFeesListItems(
|
||||
transactionDetailsViewModel.transactionInfo, rawTransaction);
|
||||
transactionDetailsViewModel.transactionInfo,
|
||||
rawTransaction,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -46,70 +48,70 @@ class RBFDetailsPage extends BasePage {
|
||||
children: [
|
||||
Expanded(
|
||||
child: SectionStandardList(
|
||||
sectionCount: 1,
|
||||
itemCounter: (int _) => transactionDetailsViewModel.RBFListItems.length,
|
||||
itemBuilder: (__, index) {
|
||||
final item = transactionDetailsViewModel.RBFListItems[index];
|
||||
sectionCount: 1,
|
||||
itemCounter: (_) => transactionDetailsViewModel.rbfListItems.length,
|
||||
itemBuilder: (__, index) {
|
||||
final item = transactionDetailsViewModel.rbfListItems[index];
|
||||
|
||||
if (item is StandartListItem) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
Clipboard.setData(ClipboardData(text: item.value));
|
||||
showBar<void>(context, S.of(context).transaction_details_copied(item.title));
|
||||
},
|
||||
child: ListRow(title: '${item.title}:', value: item.value),
|
||||
);
|
||||
}
|
||||
if (item is StandartListItem) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
Clipboard.setData(ClipboardData(text: item.value));
|
||||
showBar<void>(context, S.of(context).transaction_details_copied(item.title));
|
||||
},
|
||||
child: ListRow(title: "${item.title}:", value: item.value),
|
||||
);
|
||||
}
|
||||
|
||||
if (item is StandardExpandableListItem) {
|
||||
return StandardExpandableList(
|
||||
title: '${item.title}: ${item.expandableItems.length}',
|
||||
expandableItems: item.expandableItems,
|
||||
);
|
||||
}
|
||||
if (item is StandardExpandableListItem) {
|
||||
return StandardExpandableList(
|
||||
title: "${item.title}: ${item.expandableItems.length}",
|
||||
expandableItems: item.expandableItems,
|
||||
);
|
||||
}
|
||||
|
||||
if (item is StandardPickerListItem) {
|
||||
return StandardPickerList(
|
||||
title: item.title,
|
||||
value: item.value,
|
||||
items: item.items,
|
||||
displayItem: item.displayItem,
|
||||
onSliderChanged: item.onSliderChanged,
|
||||
onItemSelected: item.onItemSelected,
|
||||
selectedIdx: item.selectedIdx,
|
||||
customItemIndex: item.customItemIndex,
|
||||
customValue: item.customValue,
|
||||
maxValue: item.maxValue,
|
||||
);
|
||||
}
|
||||
if (item is StandardPickerListItem) {
|
||||
return StandardPickerList(
|
||||
title: item.title,
|
||||
value: item.value,
|
||||
items: item.items,
|
||||
displayItem: item.displayItem,
|
||||
onSliderChanged: item.onSliderChanged,
|
||||
onItemSelected: item.onItemSelected,
|
||||
selectedIdx: item.selectedIdx,
|
||||
customItemIndex: item.customItemIndex,
|
||||
customValue: item.customValue,
|
||||
maxValue: item.maxValue,
|
||||
);
|
||||
}
|
||||
|
||||
if (item is TextFieldListItem) {
|
||||
return TextFieldListRow(
|
||||
title: item.title,
|
||||
value: item.value,
|
||||
onSubmitted: item.onSubmitted,
|
||||
);
|
||||
}
|
||||
if (item is TextFieldListItem) {
|
||||
return TextFieldListRow(
|
||||
title: item.title,
|
||||
value: item.value,
|
||||
onSubmitted: item.onSubmitted,
|
||||
);
|
||||
}
|
||||
|
||||
return Container();
|
||||
}),
|
||||
return Container();
|
||||
},
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(24),
|
||||
child: Observer(
|
||||
builder: (_) => LoadingPrimaryButton(
|
||||
onPressed: () async {
|
||||
transactionDetailsViewModel
|
||||
.replaceByFee(transactionDetailsViewModel.newFee.toString());
|
||||
},
|
||||
text: S.of(context).send,
|
||||
isLoading:
|
||||
transactionDetailsViewModel.sendViewModel.state is IsExecutingState,
|
||||
isDisabled: transactionDetailsViewModel.sendViewModel.state
|
||||
is ExecutedSuccessfullyState,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
textColor: Theme.of(context).colorScheme.onPrimary,
|
||||
))),
|
||||
padding: const EdgeInsets.all(24),
|
||||
child: Observer(
|
||||
builder: (_) => LoadingPrimaryButton(
|
||||
onPressed: () => transactionDetailsViewModel
|
||||
.replaceByFee(transactionDetailsViewModel.newFee.toString()),
|
||||
text: S.of(context).send,
|
||||
isLoading: transactionDetailsViewModel.sendViewModel.state is IsExecutingState,
|
||||
isDisabled:
|
||||
transactionDetailsViewModel.sendViewModel.state is ExecutedSuccessfullyState,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
textColor: Theme.of(context).colorScheme.onPrimary,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
@@ -121,7 +123,7 @@ class RBFDetailsPage extends BasePage {
|
||||
return;
|
||||
}
|
||||
|
||||
reaction((_) => transactionDetailsViewModel.sendViewModel.state, (ExecutionState state) {
|
||||
reaction((_) => transactionDetailsViewModel.sendViewModel.state, (state) {
|
||||
if (state is! IsExecutingState &&
|
||||
loadingBottomSheetContext != null &&
|
||||
loadingBottomSheetContext!.mounted) {
|
||||
@@ -131,35 +133,35 @@ class RBFDetailsPage extends BasePage {
|
||||
if (state is FailureState) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
showPopUp<void>(
|
||||
context: context,
|
||||
builder: (BuildContext popupContext) {
|
||||
return AlertWithOneAction(
|
||||
alertTitle: S.of(popupContext).error,
|
||||
alertContent: state.error,
|
||||
buttonText: S.of(popupContext).ok,
|
||||
buttonAction: () => Navigator.of(popupContext).pop());
|
||||
});
|
||||
context: context,
|
||||
builder: (popupContext) => AlertWithOneAction(
|
||||
alertTitle: S.of(popupContext).error,
|
||||
alertContent: state.error,
|
||||
buttonText: S.of(popupContext).ok,
|
||||
buttonAction: () => Navigator.of(popupContext).pop(),
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
if (state is AwaitingConfirmationState) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
showPopUp<void>(
|
||||
context: context,
|
||||
builder: (BuildContext popupContext) {
|
||||
return AlertWithTwoActions(
|
||||
alertTitle: state.title ?? '',
|
||||
alertContent: state.message ?? '',
|
||||
rightButtonText: S.of(context).ok,
|
||||
leftButtonText: S.of(context).cancel,
|
||||
actionRightButton: () {
|
||||
state.onConfirm?.call();
|
||||
Navigator.of(popupContext).pop();
|
||||
},
|
||||
actionLeftButton: () {
|
||||
state.onCancel?.call();
|
||||
Navigator.of(popupContext).pop();
|
||||
});
|
||||
});
|
||||
context: context,
|
||||
builder: (popupContext) => AlertWithTwoActions(
|
||||
alertTitle: state.title ?? "",
|
||||
alertContent: state.message ?? "",
|
||||
rightButtonText: S.of(context).ok,
|
||||
leftButtonText: S.of(context).cancel,
|
||||
actionRightButton: () {
|
||||
state.onConfirm?.call();
|
||||
Navigator.of(popupContext).pop();
|
||||
},
|
||||
actionLeftButton: () {
|
||||
state.onCancel?.call();
|
||||
Navigator.of(popupContext).pop();
|
||||
},
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -169,7 +171,7 @@ class RBFDetailsPage extends BasePage {
|
||||
showModalBottomSheet<void>(
|
||||
context: context,
|
||||
isDismissible: false,
|
||||
builder: (BuildContext context) {
|
||||
builder: (context) {
|
||||
loadingBottomSheetContext = context;
|
||||
return LoadingBottomSheet(
|
||||
titleText: S.of(context).generating_transaction,
|
||||
@@ -187,42 +189,42 @@ class RBFDetailsPage extends BasePage {
|
||||
context: context,
|
||||
isDismissible: false,
|
||||
isScrollControlled: true,
|
||||
builder: (BuildContext bottomSheetContext) {
|
||||
return ConfirmSendingBottomSheet(
|
||||
key: ValueKey('rbf_confirm_sending_bottom_sheet'),
|
||||
titleText: S.of(bottomSheetContext).confirm_transaction,
|
||||
isSlideActionEnabled: transactionDetailsViewModel.sendViewModel.isReadyForSend,
|
||||
walletType: transactionDetailsViewModel.sendViewModel.walletType,
|
||||
titleIconPath:
|
||||
transactionDetailsViewModel.sendViewModel.selectedCryptoCurrency.iconPath,
|
||||
currency: transactionDetailsViewModel.sendViewModel.selectedCryptoCurrency,
|
||||
amount: S.of(bottomSheetContext).send_amount,
|
||||
amountValue:
|
||||
transactionDetailsViewModel.sendViewModel.pendingTransaction!.amountFormatted,
|
||||
fiatAmountValue: transactionDetailsViewModel
|
||||
.sendViewModel.pendingTransactionFiatAmountFormatted,
|
||||
fee: S.of(bottomSheetContext).send_fee,
|
||||
feeValue:
|
||||
transactionDetailsViewModel.sendViewModel.pendingTransaction!.feeFormatted,
|
||||
feeFiatAmount: transactionDetailsViewModel
|
||||
.sendViewModel.pendingTransactionFeeFiatAmountFormatted,
|
||||
outputs: transactionDetailsViewModel.sendViewModel.outputs,
|
||||
footerType: FooterType.slideActionButton,
|
||||
accessibleNavigationModeSlideActionButtonText: S.of(context).send,
|
||||
onSlideActionComplete: () async {
|
||||
Navigator.of(bottomSheetContext).pop();
|
||||
await transactionDetailsViewModel.sendViewModel.commitTransaction(context);
|
||||
try {
|
||||
builder: (bottomSheetContext) => ConfirmSendingBottomSheet(
|
||||
key: const ValueKey("rbf_confirm_sending_bottom_sheet"),
|
||||
titleText: S.of(bottomSheetContext).confirm_transaction,
|
||||
isSlideActionEnabled: transactionDetailsViewModel.sendViewModel.isReadyForSend,
|
||||
walletType: transactionDetailsViewModel.sendViewModel.walletType,
|
||||
titleIconPath:
|
||||
transactionDetailsViewModel.sendViewModel.selectedCryptoCurrency.iconPath,
|
||||
currency: transactionDetailsViewModel.sendViewModel.selectedCryptoCurrency,
|
||||
amount: S.of(bottomSheetContext).send_amount,
|
||||
amountValue:
|
||||
transactionDetailsViewModel.sendViewModel.pendingTransaction!.amountFormatted,
|
||||
fiatAmountValue:
|
||||
transactionDetailsViewModel.sendViewModel.pendingTransactionFiatAmountFormatted,
|
||||
fee: S.of(bottomSheetContext).send_fee,
|
||||
feeValue:
|
||||
transactionDetailsViewModel.sendViewModel.pendingTransaction!.feeFormatted,
|
||||
feeFiatAmount: transactionDetailsViewModel
|
||||
.sendViewModel.pendingTransactionFeeFiatAmountFormatted,
|
||||
outputs: transactionDetailsViewModel.sendViewModel.outputs,
|
||||
footerType: FooterType.slideActionButton,
|
||||
accessibleNavigationModeSlideActionButtonText: S.of(context).send,
|
||||
onSlideActionComplete: () async {
|
||||
Navigator.of(bottomSheetContext).pop();
|
||||
await transactionDetailsViewModel.sendViewModel.commitTransaction(context);
|
||||
try {
|
||||
if (bottomSheetContext.mounted) {
|
||||
Navigator.of(bottomSheetContext).pop();
|
||||
} catch (_) {}
|
||||
},
|
||||
change: transactionDetailsViewModel.sendViewModel.pendingTransaction!.change,
|
||||
amountParsingProxy: transactionDetailsViewModel.sendViewModel.amountParsingProxy,
|
||||
);
|
||||
},
|
||||
}
|
||||
} catch (_) {}
|
||||
},
|
||||
change: transactionDetailsViewModel.sendViewModel.pendingTransaction!.change,
|
||||
amountParsingProxy: transactionDetailsViewModel.sendViewModel.amountParsingProxy,
|
||||
),
|
||||
);
|
||||
if (result == null) {
|
||||
transactionDetailsViewModel.sendViewModel.dismissTransaction();
|
||||
await transactionDetailsViewModel.sendViewModel.dismissTransaction();
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -232,14 +234,14 @@ class RBFDetailsPage extends BasePage {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (context.mounted) {
|
||||
showPopUp<void>(
|
||||
context: context,
|
||||
builder: (BuildContext popupContext) {
|
||||
return AlertWithOneAction(
|
||||
alertTitle: S.of(popupContext).sending,
|
||||
alertContent: S.of(popupContext).transaction_sent,
|
||||
buttonText: S.of(popupContext).ok,
|
||||
buttonAction: () => Navigator.of(popupContext).pop());
|
||||
});
|
||||
context: context,
|
||||
builder: (popupContext) => AlertWithOneAction(
|
||||
alertTitle: S.of(popupContext).sending,
|
||||
alertContent: S.of(popupContext).transaction_sent,
|
||||
buttonText: S.of(popupContext).ok,
|
||||
buttonAction: () => Navigator.of(popupContext).pop(),
|
||||
),
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -131,6 +131,7 @@ class WalletConnectConnectionsView extends StatelessWidget {
|
||||
size: 40,
|
||||
onPressed: () => Navigator.of(context).maybePop(),
|
||||
icon: Icon(Icons.arrow_back_ios_new, size: 16),
|
||||
semanticLabel: S.of(context).seed_alert_back,
|
||||
iconColor: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
|
||||
@@ -300,6 +300,7 @@ class _WalletKeysPageBodyState extends State<WalletKeysPageBody>
|
||||
color: Theme.of(context).colorScheme.surfaceContainer,
|
||||
),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
@@ -313,21 +314,25 @@ class _WalletKeysPageBodyState extends State<WalletKeysPageBody>
|
||||
const SizedBox(width: 6),
|
||||
Expanded(
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Observer(
|
||||
builder: (BuildContext context) {
|
||||
return Text(
|
||||
(widget.walletKeysViewModel.obscurePassphrase)
|
||||
? "*****"
|
||||
: widget.walletKeysViewModel.passphrase,
|
||||
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
);
|
||||
},
|
||||
Expanded(
|
||||
child: Observer(
|
||||
builder: (BuildContext context) {
|
||||
return Text(
|
||||
(widget.walletKeysViewModel.obscurePassphrase)
|
||||
? "*****"
|
||||
: widget.walletKeysViewModel.passphrase,
|
||||
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 6),
|
||||
Observer(
|
||||
builder: (BuildContext context) {
|
||||
return GestureDetector(
|
||||
|
||||
@@ -20,6 +20,7 @@ class AddPassphraseBottomSheet extends StatefulWidget {
|
||||
class _AddPassphraseBottomSheetState extends State<AddPassphraseBottomSheet> {
|
||||
late final TextEditingController passphraseController;
|
||||
late final TextEditingController confirmPassphraseController;
|
||||
final _passphraseFormKey = GlobalKey<FormState>();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@@ -112,51 +113,60 @@ class _AddPassphraseBottomSheetState extends State<AddPassphraseBottomSheet> {
|
||||
),
|
||||
),
|
||||
SizedBox(height: 24),
|
||||
BaseTextFormField(
|
||||
key: ValueKey('add_passphrase_bottom_sheet_widget_passphrase_textfield_key'),
|
||||
controller: passphraseController,
|
||||
obscureText: obscurePassphrase,
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 12),
|
||||
hintText: S.of(context).required_passphrase,
|
||||
suffixIcon: GestureDetector(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
obscurePassphrase = !obscurePassphrase;
|
||||
});
|
||||
},
|
||||
child: Icon(
|
||||
obscurePassphrase ? Icons.visibility_off : Icons.visibility,
|
||||
size: 24,
|
||||
color: Theme.of(context).colorScheme.onSurface.withOpacity(0.6),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 8),
|
||||
BaseTextFormField(
|
||||
key: ValueKey('add_passphrase_bottom_sheet_widget_confirm_passphrase_textfield_key'),
|
||||
controller: confirmPassphraseController,
|
||||
obscureText: obscurePassphrase,
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 12),
|
||||
hintText: S.of(context).confirm_passphrase,
|
||||
suffixIcon: GestureDetector(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
obscurePassphrase = !obscurePassphrase;
|
||||
});
|
||||
},
|
||||
child: Icon(
|
||||
obscurePassphrase ? Icons.visibility_off : Icons.visibility,
|
||||
size: 24,
|
||||
color: Theme.of(context).colorScheme.onSurface.withOpacity(0.6),
|
||||
),
|
||||
),
|
||||
validator: (text) {
|
||||
if (text == passphraseController.text) {
|
||||
return null;
|
||||
}
|
||||
Form(
|
||||
key: _passphraseFormKey,
|
||||
child: Column(
|
||||
children: [
|
||||
BaseTextFormField(
|
||||
key: ValueKey('add_passphrase_bottom_sheet_widget_passphrase_textfield_key'),
|
||||
controller: passphraseController,
|
||||
obscureText: obscurePassphrase,
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 12),
|
||||
hintText: S.of(context).required_passphrase,
|
||||
suffixIcon: GestureDetector(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
obscurePassphrase = !obscurePassphrase;
|
||||
});
|
||||
},
|
||||
child: Icon(
|
||||
obscurePassphrase ? Icons.visibility_off : Icons.visibility,
|
||||
size: 24,
|
||||
color: Theme.of(context).colorScheme.onSurface.withOpacity(0.6),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 8),
|
||||
BaseTextFormField(
|
||||
key: ValueKey(
|
||||
'add_passphrase_bottom_sheet_widget_confirm_passphrase_textfield_key',
|
||||
),
|
||||
controller: confirmPassphraseController,
|
||||
obscureText: obscurePassphrase,
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 12),
|
||||
hintText: S.of(context).confirm_passphrase,
|
||||
suffixIcon: GestureDetector(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
obscurePassphrase = !obscurePassphrase;
|
||||
});
|
||||
},
|
||||
child: Icon(
|
||||
obscurePassphrase ? Icons.visibility_off : Icons.visibility,
|
||||
size: 24,
|
||||
color: Theme.of(context).colorScheme.onSurface.withOpacity(0.6),
|
||||
),
|
||||
),
|
||||
validator: (text) {
|
||||
if (text == passphraseController.text) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return S.of(context).passphrases_doesnt_match;
|
||||
},
|
||||
return S.of(context).passphrases_doesnt_match;
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(height: 16),
|
||||
Padding(
|
||||
@@ -184,6 +194,11 @@ class _AddPassphraseBottomSheetState extends State<AddPassphraseBottomSheet> {
|
||||
child: PrimaryButton(
|
||||
key: ValueKey('add_passphrase_bottom_sheet_widget_restore_button_key'),
|
||||
onPressed: () {
|
||||
if (_passphraseFormKey.currentState != null &&
|
||||
!_passphraseFormKey.currentState!.validate()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Navigator.pop(context);
|
||||
widget.onRestoreButtonPressed(passphraseController.text);
|
||||
},
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/utils/responsive_layout_util.dart';
|
||||
import 'package:dotted_border/dotted_border.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
@@ -92,35 +93,53 @@ class LoadingPrimaryButton extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ConstrainedBox(
|
||||
constraints: BoxConstraints(maxWidth: ResponsiveLayoutUtilBase.kDesktopMaxWidthConstraint),
|
||||
child: SizedBox(
|
||||
width: double.infinity,
|
||||
height: 52.0,
|
||||
child: TextButton(
|
||||
onPressed: (isLoading || isDisabled)
|
||||
? null
|
||||
: () {
|
||||
FocusScope.of(context).unfocus();
|
||||
onPressed.call();
|
||||
},
|
||||
style: ButtonStyle(
|
||||
backgroundColor: WidgetStateProperty.all(isDisabled ? color.withOpacity(0.5) : color),
|
||||
shape: WidgetStateProperty.all<RoundedRectangleBorder>(
|
||||
RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(18.0),
|
||||
),
|
||||
)),
|
||||
child: isLoading
|
||||
? CupertinoActivityIndicator(animating: true, color: textColor)
|
||||
: Text(
|
||||
text,
|
||||
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||||
fontSize: 15.0,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: isDisabled ? textColor.withOpacity(0.5) : textColor,
|
||||
),
|
||||
),
|
||||
final isEnabled = !isLoading && !isDisabled;
|
||||
|
||||
// The spinner replaces the label while loading, so the name is kept on a single
|
||||
// semantics node that also reports the loading and disabled states.
|
||||
return Semantics(
|
||||
button: true,
|
||||
enabled: isEnabled,
|
||||
label: text,
|
||||
value: isLoading ? S.of(context).loading : null,
|
||||
onTap: isEnabled
|
||||
? () {
|
||||
FocusScope.of(context).unfocus();
|
||||
onPressed.call();
|
||||
}
|
||||
: null,
|
||||
excludeSemantics: true,
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(maxWidth: ResponsiveLayoutUtilBase.kDesktopMaxWidthConstraint),
|
||||
child: SizedBox(
|
||||
width: double.infinity,
|
||||
height: 52.0,
|
||||
child: TextButton(
|
||||
onPressed: (isLoading || isDisabled)
|
||||
? null
|
||||
: () {
|
||||
FocusScope.of(context).unfocus();
|
||||
onPressed.call();
|
||||
},
|
||||
style: ButtonStyle(
|
||||
backgroundColor:
|
||||
WidgetStateProperty.all(isDisabled ? color.withOpacity(0.5) : color),
|
||||
shape: WidgetStateProperty.all<RoundedRectangleBorder>(
|
||||
RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(18.0),
|
||||
),
|
||||
)),
|
||||
child: isLoading
|
||||
? CupertinoActivityIndicator(animating: true, color: textColor)
|
||||
: Text(
|
||||
text,
|
||||
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||||
fontSize: 15.0,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: isDisabled ? textColor.withOpacity(0.5) : textColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user