diff --git a/README.md b/README.md index 3077366ac..0fb45f6de 100644 --- a/README.md +++ b/README.md @@ -5,9 +5,10 @@ ## Links * Website: https://cakewallet.com -* App Store: https://cakewallet.com/ios +* App Store (iOS / MacOS): https://cakewallet.com/ios * Google Play: https://cakewallet.com/gp * APK: https://github.com/cake-tech/cake_wallet/releases +* Linux: https://github.com/cake-tech/cake_wallet/releases ## Features @@ -15,18 +16,22 @@ * Completely noncustodial. *Your keys, your coins.* * Built-in exchange for dozens of pairs -* Buy cryptocurrency with credit/debit/bank +* Easily pay cryptocurrency invoices with fixed rate exchanges +* Buy cryptocurrency (BTC/LTC/XMR) with credit/debit/bank * Sell cryptocurrency by bank transfer +* Purchase gift cards at a discount using only an email with [Cake Pay](https://cakepay.com), available in-app * Scan QR codes for easy cryptocurrency transfers * Create several wallets * Select your own custom nodes/servers * Address book * Backup to external location or iCloud * Send to OpenAlias, Unstoppable Domains, Yats, and FIO Crypto Handles -* Set custom fee levels +* Set desired network fee level * Store local transaction notes * Extremely simple user experience * Convenient exchange and sending templates for recurring payments +* Create donation links and invoices in the receive screen +* Robust privacy settings (eg: Tor-only connections) ### Monero Specific Features @@ -34,13 +39,13 @@ * Full support for Monero subaddresses and accounts * Specify restore height for faster syncing * Specify multiple recipients for batch sending +* Optionally set Monero nodes as trusted for faster syncing ### Bitcoin Specific Features * Bitcoin coin control (specify specific outputs to spend) * Automatically generate new addresses * Specify multiple recipients for batch sending -* Buy BTC with over a dozen fiat currencies * Sell BTC for USD ### Litecoin Specific Features @@ -48,7 +53,6 @@ * Litecoin coin control (specify specific outputs to spend) * Automatically generate new addresses * Specify multiple recipients for batch sending -* Buy LTC with over a dozen fiat currencies ### Haven Specific Features @@ -63,7 +67,7 @@ ## Links * Website: https://monero.com -* App Store: https://apps.apple.com/app/id1601990386 +* App Store (iOS): https://apps.apple.com/app/id1601990386 * Google Play: https://play.google.com/store/apps/details?id=com.monero.app * APK: https://github.com/cake-tech/cake_wallet/releases @@ -71,6 +75,8 @@ We have 24/7 free support. Please contact support@cakewallet.com +We have excellent user guides, which are also open-source and open for contributions: https://guides.cakewallet.com + # Build Instructions More instructions to follow @@ -136,3 +142,7 @@ The only parts to be translated, if needed, are the values m and s after the var 3. Add the raw mapping underneath in `lib/entities/fiat_currency.dart` following the same format as the others. 4. Add a flag of the issuing country or organization to `assets/images/flags/XXXX.png`, replacing XXXX with the ISO 3166-1 alpha-3 code used above (eg: `usa.png`, `eur.png`). Do not add this if the flag with the same name already exists. The image must be 42x26 pixels with a 3 pixels of transparent margin on all 4 sides. + +--- + +Copyright (C) 2018-2023 Cake Labs LLC diff --git a/assets/text/Monerocom_Release_Notes.txt b/assets/text/Monerocom_Release_Notes.txt index be218630d..56fb3deb7 100644 --- a/assets/text/Monerocom_Release_Notes.txt +++ b/assets/text/Monerocom_Release_Notes.txt @@ -1 +1,3 @@ -Fix Restore from QR code \ No newline at end of file +Reliability fixes for PIN login, transaction appearance, keyboard inputs, and QR codes +Show amount received by each Monero account in account overview +Other bugfixes \ No newline at end of file diff --git a/assets/text/Release_Notes.txt b/assets/text/Release_Notes.txt index d9acd464f..56fb3deb7 100644 --- a/assets/text/Release_Notes.txt +++ b/assets/text/Release_Notes.txt @@ -1,4 +1,3 @@ -Fix for QR codes -Fix for creating sub-addresses -Fix Add/Edit nodes -Fix issues with text/amount fields \ No newline at end of file +Reliability fixes for PIN login, transaction appearance, keyboard inputs, and QR codes +Show amount received by each Monero account in account overview +Other bugfixes \ No newline at end of file diff --git a/cw_bitcoin/lib/electrum.dart b/cw_bitcoin/lib/electrum.dart index 81f2da161..70b072f7b 100644 --- a/cw_bitcoin/lib/electrum.dart +++ b/cw_bitcoin/lib/electrum.dart @@ -88,7 +88,7 @@ class ElectrumClient { unterminatedString = ''; } } on TypeError catch (e) { - if (!e.toString().contains('Map') || !e.toString().contains('Map')) { + if (!e.toString().contains('Map') && !e.toString().contains('Map')) { return; } diff --git a/ios/Podfile.lock b/ios/Podfile.lock index d0e05f5dd..62074faed 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -113,7 +113,7 @@ PODS: - OrderedSet (~> 5.0) - flutter_mailer (0.0.1): - Flutter - - flutter_secure_storage (3.3.1): + - flutter_secure_storage (6.0.0): - Flutter - in_app_review (0.2.0): - Flutter @@ -264,19 +264,19 @@ SPEC CHECKSUMS: Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 flutter_inappwebview: bfd58618f49dc62f2676de690fc6dcda1d6c3721 flutter_mailer: 2ef5a67087bc8c6c4cefd04a178bf1ae2c94cd83 - flutter_secure_storage: 7953c38a04c3fdbb00571bcd87d8e3b5ceb9daec + flutter_secure_storage: 23fc622d89d073675f2eaa109381aefbcf5a49be in_app_review: 318597b3a06c22bb46dc454d56828c85f444f99d local_auth_ios: c6cf091ded637a88f24f86a8875d8b0f526e2605 MTBBarcodeScanner: f453b33c4b7dfe545d8c6484ed744d55671788cb OrderedSet: aaeb196f7fef5a9edf55d89760da9176ad40b93c package_info: 873975fc26034f0b863a300ad47e7f1ac6c7ec62 - path_provider_foundation: c68054786f1b4f3343858c1e1d0caaded73f0be9 + path_provider_foundation: eaf5b3e458fc0e5fbb9940fb09980e853fe058b8 permission_handler_apple: 44366e37eaf29454a1e7b1b7d736c2cceaeb17ce platform_device_id: 81b3e2993881f87d0c82ef151dc274df4869aef5 Reachability: 33e18b67625424e47b6cde6d202dce689ad7af96 SDWebImage: fd7e1a22f00303e058058278639bf6196ee431fe share_plus: 056a1e8ac890df3e33cb503afffaf1e9b4fbae68 - shared_preferences_foundation: 986fc17f3d3251412d18b0265f9c64113a8c2472 + shared_preferences_foundation: e2dae3258e06f44cc55f49d42024fd8dd03c590c SwiftProtobuf: afced68785854575756db965e9da52bbf3dc45e7 SwiftyGif: 93a1cc87bf3a51916001cf8f3d63835fb64c819f uni_links: d97da20c7701486ba192624d99bffaaffcfc298a diff --git a/ios/Runner/InfoBase.plist b/ios/Runner/InfoBase.plist index 13a2c24df..29cd24cb4 100644 --- a/ios/Runner/InfoBase.plist +++ b/ios/Runner/InfoBase.plist @@ -52,16 +52,6 @@ bitcoin-wallet - - CFBundleTypeRole - Editor - CFBundleURLName - bitcoin_wallet - CFBundleURLSchemes - - bitcoin_wallet - - CFBundleTypeRole Editor @@ -82,16 +72,6 @@ monero-wallet - - CFBundleTypeRole - Editor - CFBundleURLName - monero_wallet - CFBundleURLSchemes - - monero_wallet - - CFBundleTypeRole Editor @@ -112,16 +92,6 @@ litecoin-wallet - - CFBundleTypeRole - Viewer - CFBundleURLName - litecoin_wallet - CFBundleURLSchemes - - litecoin_wallet - - CFBundleVersion $(CURRENT_PROJECT_VERSION) diff --git a/lib/buy/onramper/onramper_buy_provider.dart b/lib/buy/onramper/onramper_buy_provider.dart index a887f98dc..faf2e6da7 100644 --- a/lib/buy/onramper/onramper_buy_provider.dart +++ b/lib/buy/onramper/onramper_buy_provider.dart @@ -1,6 +1,7 @@ import 'package:cake_wallet/.secrets.g.dart' as secrets; import 'package:cake_wallet/store/settings_store.dart'; import 'package:cake_wallet/themes/theme_base.dart'; +import 'package:cw_core/crypto_currency.dart'; import 'package:cw_core/wallet_base.dart'; class OnRamperBuyProvider { @@ -13,7 +14,16 @@ class OnRamperBuyProvider { static const _baseUrl = 'buy.onramper.com'; - static String get _apiKey => secrets.onramperApiKey; + String get _apiKey => secrets.onramperApiKey; + + String get _normalizeCryptoCurrency { + switch (_wallet.currency) { + case CryptoCurrency.ltc: + return "LTC_LITECOIN"; + default: + return _wallet.currency.title; + } + } Uri requestUrl() { String primaryColor, @@ -53,7 +63,7 @@ class OnRamperBuyProvider { return Uri.https(_baseUrl, '', { 'apiKey': _apiKey, - 'defaultCrypto': _wallet.currency.title, + 'defaultCrypto': _normalizeCryptoCurrency, 'defaultFiat': _settingsStore.fiatCurrency.title, 'wallets': '${_wallet.currency.title}:${_wallet.walletAddresses.address}', 'supportSell': "false", diff --git a/lib/core/address_validator.dart b/lib/core/address_validator.dart index 0bed0611c..57318254a 100644 --- a/lib/core/address_validator.dart +++ b/lib/core/address_validator.dart @@ -1,4 +1,4 @@ -import 'package:flutter/foundation.dart'; +import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin; import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/core/validator.dart'; import 'package:cw_core/crypto_currency.dart'; @@ -7,6 +7,9 @@ class AddressValidator extends TextValidator { AddressValidator({required CryptoCurrency type}) : super( errorMessage: S.current.error_text_address, + useAdditionalValidation: type == CryptoCurrency.btc + ? bitcoin.Address.validateAddress + : null, pattern: getPattern(type), length: getLength(type)); @@ -18,8 +21,7 @@ class AddressValidator extends TextValidator { return '^[0-9a-zA-Z]{59}\$|^[0-9a-zA-Z]{92}\$|^[0-9a-zA-Z]{104}\$' '|^[0-9a-zA-Z]{105}\$|^addr1[0-9a-zA-Z]{98}\$'; case CryptoCurrency.btc: - return '^1[0-9a-zA-Z]{32}\$|^1[0-9a-zA-Z]{33}\$|^3[0-9a-zA-Z]{32}\$' - '|^3[0-9a-zA-Z]{33}\$|^bc1[0-9a-zA-Z]{39}\$|^bc1[0-9a-zA-Z]{59}\$'; + return '^3[0-9a-zA-Z]{32}\$|^3[0-9a-zA-Z]{33}\$|^bc1[0-9a-zA-Z]{59}\$'; case CryptoCurrency.nano: return '[0-9a-zA-Z_]'; case CryptoCurrency.usdc: @@ -63,7 +65,7 @@ class AddressValidator extends TextValidator { case CryptoCurrency.bch: case CryptoCurrency.bnb: return '[0-9a-zA-Z]'; - case CryptoCurrency.hbar: + case CryptoCurrency.hbar: return '[0-9a-zA-Z.]'; case CryptoCurrency.zaddr: return '^zs[0-9a-zA-Z]{75}'; @@ -165,9 +167,9 @@ class AddressValidator extends TextValidator { return [34]; case CryptoCurrency.hbar: return [4, 5, 6, 7, 8, 9, 10, 11]; - case CryptoCurrency.xvg: + case CryptoCurrency.xvg: return [34]; - case CryptoCurrency.zen: + case CryptoCurrency.zen: return [35]; case CryptoCurrency.zaddr: return null; diff --git a/lib/core/backup_service.dart b/lib/core/backup_service.dart index 20fd753d8..2870c4488 100644 --- a/lib/core/backup_service.dart +++ b/lib/core/backup_service.dart @@ -210,6 +210,8 @@ class BackupService { final currentFiatCurrency = data[PreferencesKey.currentFiatCurrencyKey] as String?; final shouldSaveRecipientAddress = data[PreferencesKey.shouldSaveRecipientAddressKey] as bool?; final isAppSecure = data[PreferencesKey.isAppSecureKey] as bool?; + final disableBuy = data[PreferencesKey.disableBuyKey] as bool?; + final disableSell = data[PreferencesKey.disableSellKey] as bool?; final currentTransactionPriorityKeyLegacy = data[PreferencesKey.currentTransactionPriorityKeyLegacy] as int?; final allowBiometricalAuthentication = data[PreferencesKey.allowBiometricalAuthenticationKey] as bool?; final currentBitcoinElectrumSererId = data[PreferencesKey.currentBitcoinElectrumSererIdKey] as int?; @@ -251,6 +253,16 @@ class BackupService { PreferencesKey.isAppSecureKey, isAppSecure); + if (disableBuy != null) + await _sharedPreferences.setBool( + PreferencesKey.disableBuyKey, + disableBuy); + + if (disableSell != null) + await _sharedPreferences.setBool( + PreferencesKey.disableSellKey, + disableSell); + if (currentTransactionPriorityKeyLegacy != null) await _sharedPreferences.setInt( PreferencesKey.currentTransactionPriorityKeyLegacy, @@ -421,6 +433,10 @@ class BackupService { _sharedPreferences.getString(PreferencesKey.currentFiatCurrencyKey), PreferencesKey.shouldSaveRecipientAddressKey: _sharedPreferences .getBool(PreferencesKey.shouldSaveRecipientAddressKey), + PreferencesKey.disableBuyKey: _sharedPreferences + .getBool(PreferencesKey.disableBuyKey), + PreferencesKey.disableSellKey: _sharedPreferences + .getBool(PreferencesKey.disableSellKey), PreferencesKey.isDarkThemeLegacy: _sharedPreferences.getBool(PreferencesKey.isDarkThemeLegacy), PreferencesKey.currentPinLength: diff --git a/lib/core/validator.dart b/lib/core/validator.dart index 96c382b7d..34e985991 100644 --- a/lib/core/validator.dart +++ b/lib/core/validator.dart @@ -1,24 +1,26 @@ -import 'package:flutter/foundation.dart'; - abstract class Validator { - Validator({required this.errorMessage}); + Validator({required this.errorMessage, this.useAdditionalValidation}); final String errorMessage; + final bool Function(T)? useAdditionalValidation; bool isValid(T? value); - String? call(T? value) => !isValid(value) ? errorMessage : null; + String? call(T? value) => !isValid(value) ? errorMessage : null; } class TextValidator extends Validator { - TextValidator( - {this.minLength, - this.maxLength, - this.pattern, - String errorMessage = '', - this.length, - this.isAutovalidate = false}) - : super(errorMessage: errorMessage); + TextValidator({ + bool Function(String)? useAdditionalValidation, + this.minLength, + this.maxLength, + this.pattern, + String errorMessage = '', + this.length, + this.isAutovalidate = false, + }) : super( + errorMessage: errorMessage, + useAdditionalValidation: useAdditionalValidation); final int? minLength; final int? maxLength; @@ -32,11 +34,26 @@ class TextValidator extends Validator { return isAutovalidate ? true : false; } - return value.length > (minLength ?? 0) && - (length?.contains(value.length) ?? true) && - ((maxLength ?? 0) > 0 ? (value.length <= maxLength!) : true) && - (pattern != null ? match(value) : true); + final greaterThanMinLength = value.length > (minLength ?? 0); + if (!greaterThanMinLength) return false; + + final lengthMatched = length?.contains(value.length) ?? true; + if (!lengthMatched) return false; + + final lowerThanMaxLength = + (maxLength ?? 0) > 0 ? (value.length <= maxLength!) : true; + if (!lowerThanMaxLength) return false; + + if (pattern == null) return true; + + final valueMatched = match(value); + final valueValidated = useAdditionalValidation != null + ? useAdditionalValidation!(value) || valueMatched + : valueMatched; + + return valueValidated; } - bool match(String value) => pattern != null ? RegExp(pattern!).hasMatch(value) : false; + bool match(String value) => + pattern != null ? RegExp(pattern!).hasMatch(value) : false; } diff --git a/lib/entities/main_actions.dart b/lib/entities/main_actions.dart index ec70b95d9..f7a322096 100644 --- a/lib/entities/main_actions.dart +++ b/lib/entities/main_actions.dart @@ -47,23 +47,23 @@ class MainActions { switch (walletType) { case WalletType.bitcoin: case WalletType.litecoin: - if (DeviceInfo.instance.isMobile) { - Navigator.of(context).pushNamed(Routes.onramperPage); - } else { - final uri = getIt - .get() - .requestUrl(); - await launchUrl(uri); + if (viewModel.isEnabledBuyAction) { + if (DeviceInfo.instance.isMobile) { + Navigator.of(context).pushNamed(Routes.onramperPage); + } else { + final uri = getIt.get().requestUrl(); + await launchUrl(uri); + } } break; case WalletType.monero: - if (DeviceInfo.instance.isMobile) { - Navigator.of(context).pushNamed(Routes.payfuraPage); - } else { - final uri = getIt - .get() - .requestUrl(); - await launchUrl(uri); + if (viewModel.isEnabledBuyAction) { + if (DeviceInfo.instance.isMobile) { + Navigator.of(context).pushNamed(Routes.payfuraPage); + } else { + final uri = getIt.get().requestUrl(); + await launchUrl(uri); + } } break; default: @@ -118,12 +118,14 @@ class MainActions { switch (walletType) { case WalletType.bitcoin: - final moonPaySellProvider = MoonPaySellProvider(); - final uri = await moonPaySellProvider.requestUrl( - currency: viewModel.wallet.currency, - refundWalletAddress: viewModel.wallet.walletAddresses.address, - ); - await launchUrl(uri); + if (viewModel.isEnabledSellAction) { + final moonPaySellProvider = MoonPaySellProvider(); + final uri = await moonPaySellProvider.requestUrl( + currency: viewModel.wallet.currency, + refundWalletAddress: viewModel.wallet.walletAddresses.address, + ); + await launchUrl(uri); + } break; default: await showPopUp( diff --git a/lib/entities/preferences_key.dart b/lib/entities/preferences_key.dart index 90b57668d..21966a06a 100644 --- a/lib/entities/preferences_key.dart +++ b/lib/entities/preferences_key.dart @@ -10,6 +10,8 @@ class PreferencesKey { static const currentBalanceDisplayModeKey = 'current_balance_display_mode'; static const shouldSaveRecipientAddressKey = 'save_recipient_address'; static const isAppSecureKey = 'is_app_secure'; + static const disableBuyKey = 'disable_buy'; + static const disableSellKey = 'disable_sell'; static const currentFiatApiModeKey = 'current_fiat_api_mode'; static const allowBiometricalAuthenticationKey = 'allow_biometrical_authentication'; diff --git a/lib/router.dart b/lib/router.dart index 661a827e7..ccafe6abc 100644 --- a/lib/router.dart +++ b/lib/router.dart @@ -169,6 +169,7 @@ Route createRoute(RouteSettings settings) { fullscreenDialog: true); } else if (isSingleCoin) { return MaterialPageRoute( + fullscreenDialog: true, builder: (_) => getIt.get( param1: availableWalletTypes.first )); @@ -188,6 +189,7 @@ Route createRoute(RouteSettings settings) { case Routes.restoreWallet: return MaterialPageRoute( + fullscreenDialog: true, builder: (_) => getIt.get( param1: settings.arguments as WalletType)); @@ -509,7 +511,9 @@ Route createRoute(RouteSettings settings) { case Routes.anonPayInvoicePage: final args = settings.arguments as List; - return CupertinoPageRoute(builder: (_) => getIt.get(param1: args)); + return CupertinoPageRoute( + fullscreenDialog: true, + builder: (_) => getIt.get(param1: args)); case Routes.anonPayReceivePage: final anonInvoiceViewData = settings.arguments as AnonpayInfoBase; diff --git a/lib/src/screens/contact/contact_page.dart b/lib/src/screens/contact/contact_page.dart index abc39bda8..09dc5efc3 100644 --- a/lib/src/screens/contact/contact_page.dart +++ b/lib/src/screens/contact/contact_page.dart @@ -1,13 +1,11 @@ -import 'package:cake_wallet/core/validator.dart'; +import 'package:cake_wallet/core/address_validator.dart'; import 'package:cake_wallet/palette.dart'; import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:cw_core/currency.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/cupertino.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:mobx/mobx.dart'; import 'package:cake_wallet/generated/i18n.dart'; -import 'package:cake_wallet/core/address_validator.dart'; import 'package:cake_wallet/core/contact_name_validator.dart'; import 'package:cake_wallet/core/execution_state.dart'; import 'package:cake_wallet/view_model/contact_list/contact_view_model.dart'; @@ -33,8 +31,8 @@ class ContactPage extends BasePage { _addressController .addListener(() => contactViewModel.address = _addressController.text); - autorun((_) => - _currencyTypeController.text = contactViewModel.currency?.toString()??''); + autorun((_) => _currencyTypeController.text = + contactViewModel.currency?.toString() ?? ''); } @override @@ -61,96 +59,105 @@ class ContactPage extends BasePage { } }); - return ScrollableWithBottomSection( - contentPadding: EdgeInsets.all(24), - content: Form( - key: _formKey, - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - BaseTextFormField( - controller: _nameController, - hintText: S.of(context).contact_name, - validator: ContactNameValidator()), - Padding( - padding: EdgeInsets.only(top: 20), - child: Container( - child: InkWell( - onTap: () => _presentCurrencyPicker(context), - child: IgnorePointer( - child: BaseTextFormField( - controller: _currencyTypeController, - hintText: S.of(context).settings_currency, - suffixIcon: Row( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.end, - children: [downArrow], - ), - )), + return Observer( + builder: (_) => ScrollableWithBottomSection( + contentPadding: EdgeInsets.all(24), + content: Form( + key: _formKey, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + BaseTextFormField( + controller: _nameController, + hintText: S.of(context).contact_name, + validator: ContactNameValidator()), + Padding( + padding: EdgeInsets.only(top: 20), + child: Container( + child: InkWell( + onTap: () => _presentCurrencyPicker(context), + child: IgnorePointer( + child: BaseTextFormField( + controller: _currencyTypeController, + hintText: S.of(context).settings_currency, + suffixIcon: Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.end, + children: [downArrow], + ), + )), + ), ), ), - ), - Padding( - padding: EdgeInsets.only(top: 20), - child: Observer( - builder: (_) => AddressTextField( + if (contactViewModel.currency != null) + Padding( + padding: EdgeInsets.only(top: 20), + child: AddressTextField( controller: _addressController, options: [ AddressTextFieldOption.paste, AddressTextFieldOption.qrCode, ], - buttonColor: Theme.of(context).accentTextTheme!.headline3!.color!, + buttonColor: + Theme.of(context).accentTextTheme!.headline3!.color!, iconColor: PaletteDark.gray, - borderColor: Theme.of(context).primaryTextTheme!.headline6!.backgroundColor!, - validator: TextValidator() - // AddressValidator( - // type: contactViewModel.currency), - )), - ) - ], - ), - ), - bottomSectionPadding: - EdgeInsets.only(left: 24, right: 24, bottom: 24), - bottomSection: Row( - children: [ - Expanded( - child: PrimaryButton( - onPressed: () { - contactViewModel.reset(); - _nameController.text = ''; - _addressController.text = ''; - }, - text: S.of(context).reset, - color: Colors.orange, - textColor: Colors.white), + borderColor: Theme.of(context) + .primaryTextTheme! + .headline6! + .backgroundColor!, + validator: + AddressValidator(type: contactViewModel.currency!), + ), + ) + ], ), - SizedBox(width: 20), - Expanded( - child: Observer( - builder: (_) => PrimaryButton( - onPressed: () async { - if (_formKey.currentState != null && !_formKey.currentState!.validate()) { - return; - } + ), + bottomSectionPadding: + EdgeInsets.only(left: 24, right: 24, bottom: 24), + bottomSection: Row( + children: [ + Expanded( + child: PrimaryButton( + onPressed: () { + contactViewModel.reset(); + _nameController.text = ''; + _addressController.text = ''; + }, + text: S.of(context).reset, + color: Colors.orange, + textColor: Colors.white), + ), + SizedBox(width: 20), + Expanded( + child: Observer( + builder: (_) => PrimaryButton( + onPressed: () async { + if (_formKey.currentState != null && + !_formKey.currentState!.validate()) { + return; + } - await contactViewModel.save(); - }, - text: S.of(context).save, - color: Theme.of(context).accentTextTheme!.bodyText1!.color!, - textColor: Colors.white, - isDisabled: !contactViewModel.isReady))) - ], - )); + await contactViewModel.save(); + }, + text: S.of(context).save, + color: Theme.of(context) + .accentTextTheme! + .bodyText1! + .color!, + textColor: Colors.white, + isDisabled: !contactViewModel.isReady))) + ], + )), + ); } void _presentCurrencyPicker(BuildContext context) { showPopUp( builder: (_) => CurrencyPicker( - selectedAtIndex: - contactViewModel.currency != null - ? contactViewModel.currencies.indexOf(contactViewModel.currency!) - : 0, + selectedAtIndex: contactViewModel.currency != null + ? contactViewModel.currencies + .indexOf(contactViewModel.currency!) + : -1, items: contactViewModel.currencies, title: S.of(context).please_select, hintText: S.of(context).search_currency, diff --git a/lib/src/screens/dashboard/widgets/address_page.dart b/lib/src/screens/dashboard/widgets/address_page.dart index 465c494d1..975887a7a 100644 --- a/lib/src/screens/dashboard/widgets/address_page.dart +++ b/lib/src/screens/dashboard/widgets/address_page.dart @@ -96,7 +96,10 @@ class AddressPage extends BasePage { @override Widget middle(BuildContext context) => - PresentReceiveOptionPicker(receiveOptionViewModel: receiveOptionViewModel); + PresentReceiveOptionPicker( + receiveOptionViewModel: receiveOptionViewModel, + hasWhiteBackground: currentTheme.type == ThemeType.light, + ); @override Widget Function(BuildContext, Widget) get rootWrapper => diff --git a/lib/src/screens/dashboard/widgets/filter_widget.dart b/lib/src/screens/dashboard/widgets/filter_widget.dart index 9b8c87ea3..456ed95f7 100644 --- a/lib/src/screens/dashboard/widgets/filter_widget.dart +++ b/lib/src/screens/dashboard/widgets/filter_widget.dart @@ -1,13 +1,9 @@ -import 'dart:ui'; -import 'package:cake_wallet/palette.dart'; import 'package:cake_wallet/src/screens/dashboard/widgets/filter_tile.dart'; import 'package:cake_wallet/src/widgets/section_divider.dart'; import 'package:cake_wallet/src/widgets/standard_checkbox.dart'; import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart'; -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; -import 'package:cake_wallet/src/widgets/alert_background.dart'; -import 'package:cake_wallet/src/widgets/alert_close_button.dart'; +import 'package:cake_wallet/src/widgets/picker_wrapper_widget.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; //import 'package:date_range_picker/date_range_picker.dart' as date_rage_picker; @@ -20,89 +16,93 @@ class FilterWidget extends StatelessWidget { @override Widget build(BuildContext context) { const sectionDivider = const SectionDivider(); - return AlertBackground( - child: Stack( - alignment: Alignment.center, - children: [ - Column( - mainAxisSize: MainAxisSize.min, - children: [ - Padding( - padding: EdgeInsets.only(left: 24, right: 24, top: 24), - child: ClipRRect( - borderRadius: BorderRadius.all(Radius.circular(24)), - child: Container( - color: Theme.of(context).textTheme!.bodyText1!.decorationColor!, - child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ - Padding( - padding: EdgeInsets.all(24.0), - child: Text( - S.of(context).filter_by, - style: TextStyle( - color: Theme.of(context).primaryTextTheme.overline!.color!, - fontSize: 16, - fontFamily: 'Lato', - decoration: TextDecoration.none, - ), + return PickerWrapperWidget( + children: [ + Padding( + padding: EdgeInsets.only(left: 24, right: 24, top: 24), + child: ClipRRect( + borderRadius: BorderRadius.all(Radius.circular(24)), + child: Container( + color: Theme.of(context).textTheme!.bodyText1!.decorationColor!, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: EdgeInsets.all(24.0), + child: Text( + S.of(context).filter_by, + style: TextStyle( + color: Theme.of(context) + .primaryTextTheme + .overline! + .color!, + fontSize: 16, + fontFamily: 'Lato', + decoration: TextDecoration.none, ), ), - sectionDivider, - ListView.separated( - padding: EdgeInsets.zero, - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - itemCount: dashboardViewModel.filterItems.length, - separatorBuilder: (context, _) => sectionDivider, - itemBuilder: (_, index1) { - final title = dashboardViewModel.filterItems.keys.elementAt(index1); - final section = dashboardViewModel.filterItems.values.elementAt(index1); - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: EdgeInsets.only(top: 20, left: 24, right: 24), - child: Text( - title, - style: TextStyle( - color: Theme.of(context).primaryTextTheme!.headline6!.color!, - fontSize: 16, - fontFamily: 'Lato', - fontWeight: FontWeight.bold, - decoration: TextDecoration.none), - ), + ), + sectionDivider, + ListView.separated( + padding: EdgeInsets.zero, + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemCount: dashboardViewModel.filterItems.length, + separatorBuilder: (context, _) => sectionDivider, + itemBuilder: (_, index1) { + final title = dashboardViewModel.filterItems.keys + .elementAt(index1); + final section = dashboardViewModel.filterItems.values + .elementAt(index1); + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: + EdgeInsets.only(top: 20, left: 24, right: 24), + child: Text( + title, + style: TextStyle( + color: Theme.of(context) + .primaryTextTheme! + .headline6! + .color!, + fontSize: 16, + fontFamily: 'Lato', + fontWeight: FontWeight.bold, + decoration: TextDecoration.none), ), - ListView.builder( - padding: EdgeInsets.symmetric(vertical: 8.0), - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - itemCount: section.length, - itemBuilder: (_, index2) { - final item = section[index2]; - final content = Observer( - builder: (_) => StandardCheckbox( - value: item.value(), - caption: item.caption, - gradientBackground: true, - borderColor: Theme.of(context).dividerColor, - iconColor: Colors.white, - onChanged: (value) => item.onChanged(), - )); - return FilterTile(child: content); - }, - ) - ], - ); - }, - ), - ]), - ), - ), - ), - ], + ), + ListView.builder( + padding: EdgeInsets.symmetric(vertical: 8.0), + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemCount: section.length, + itemBuilder: (_, index2) { + final item = section[index2]; + final content = Observer( + builder: (_) => StandardCheckbox( + value: item.value(), + caption: item.caption, + gradientBackground: true, + borderColor: + Theme.of(context).dividerColor, + iconColor: Colors.white, + onChanged: (value) => + item.onChanged(), + )); + return FilterTile(child: content); + }, + ) + ], + ); + }, + ), + ]), + ), ), - AlertCloseButton() - ], - ), + ) + ], ); } } diff --git a/lib/src/screens/dashboard/widgets/present_receive_option_picker.dart b/lib/src/screens/dashboard/widgets/present_receive_option_picker.dart index 8b3ffb894..7b02765b3 100644 --- a/lib/src/screens/dashboard/widgets/present_receive_option_picker.dart +++ b/lib/src/screens/dashboard/widgets/present_receive_option_picker.dart @@ -9,14 +9,22 @@ import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:cake_wallet/generated/i18n.dart'; class PresentReceiveOptionPicker extends StatelessWidget { - PresentReceiveOptionPicker({required this.receiveOptionViewModel}); + PresentReceiveOptionPicker( + {required this.receiveOptionViewModel, this.hasWhiteBackground = false}); final ReceiveOptionViewModel receiveOptionViewModel; + final bool hasWhiteBackground; @override Widget build(BuildContext context) { - final arrowBottom = - Image.asset('assets/images/arrow_bottom_purple_icon.png', color: Colors.white, height: 6); + final textIconTheme = hasWhiteBackground + ? Theme.of(context).accentTextTheme.headline2!.backgroundColor! + : Colors.white; + final arrowBottom = Image.asset( + 'assets/images/arrow_bottom_purple_icon.png', + color: textIconTheme, + height: 6, + ); return TextButton( onPressed: () => _showPicker(context), @@ -40,14 +48,14 @@ class PresentReceiveOptionPicker extends StatelessWidget { fontSize: 18.0, fontWeight: FontWeight.bold, fontFamily: 'Lato', - color: Theme.of(context).accentTextTheme.headline2!.backgroundColor!), + color: textIconTheme), ), Observer( builder: (_) => Text(receiveOptionViewModel.selectedReceiveOption.toString(), style: TextStyle( fontSize: 10.0, fontWeight: FontWeight.w500, - color: Theme.of(context).textTheme.headline5!.color!))) + color: textIconTheme))) ], ), SizedBox(width: 5), diff --git a/lib/src/screens/receive/widgets/anonpay_currency_input_field.dart b/lib/src/screens/receive/widgets/anonpay_currency_input_field.dart index 6f2d5179c..824044333 100644 --- a/lib/src/screens/receive/widgets/anonpay_currency_input_field.dart +++ b/lib/src/screens/receive/widgets/anonpay_currency_input_field.dart @@ -32,7 +32,7 @@ class AnonpayCurrencyInputField extends StatelessWidget { decoration: BoxDecoration( border: Border( bottom: BorderSide( - color: Theme.of(context).accentTextTheme.headline6!.backgroundColor!, + color: Theme.of(context).primaryTextTheme.bodyText1!.color!, width: 1)), ), child: Padding( diff --git a/lib/src/screens/receive/widgets/anonpay_input_form.dart b/lib/src/screens/receive/widgets/anonpay_input_form.dart index 974601baa..bd8a23d9c 100644 --- a/lib/src/screens/receive/widgets/anonpay_input_form.dart +++ b/lib/src/screens/receive/widgets/anonpay_input_form.dart @@ -69,7 +69,7 @@ class AnonInvoiceForm extends StatelessWidget { BaseTextFormField( controller: nameController, focusNode: _nameFocusNode, - borderColor: Theme.of(context).accentTextTheme.headline6!.backgroundColor, + borderColor: Theme.of(context).primaryTextTheme.bodyText1!.color!, suffixIcon: SizedBox(width: 36), hintText: S.of(context).optional_name, textInputAction: TextInputAction.next, @@ -88,7 +88,7 @@ class AnonInvoiceForm extends StatelessWidget { controller: descriptionController, focusNode: _descriptionFocusNode, textInputAction: TextInputAction.next, - borderColor: Theme.of(context).accentTextTheme.headline6!.backgroundColor, + borderColor: Theme.of(context).primaryTextTheme.bodyText1!.color!, suffixIcon: SizedBox(width: 36), hintText: S.of(context).optional_description, placeholderTextStyle: TextStyle( @@ -104,7 +104,7 @@ class AnonInvoiceForm extends StatelessWidget { controller: emailController, textInputAction: TextInputAction.next, focusNode: _emailFocusNode, - borderColor: Theme.of(context).accentTextTheme.headline6!.backgroundColor, + borderColor: Theme.of(context).primaryTextTheme.bodyText1!.color!, suffixIcon: SizedBox(width: 36), keyboardType: TextInputType.emailAddress, hintText: S.of(context).optional_email_hint, diff --git a/lib/src/screens/receive/widgets/currency_input_field.dart b/lib/src/screens/receive/widgets/currency_input_field.dart index 286c6f1cd..273bbb46b 100644 --- a/lib/src/screens/receive/widgets/currency_input_field.dart +++ b/lib/src/screens/receive/widgets/currency_input_field.dart @@ -1,4 +1,3 @@ -import 'package:cake_wallet/core/amount_validator.dart'; import 'package:cake_wallet/src/widgets/base_text_form_field.dart'; import 'package:cw_core/currency.dart'; import 'package:flutter/material.dart'; @@ -10,18 +9,20 @@ class CurrencyInputField extends StatelessWidget { required this.onTapPicker, required this.selectedCurrency, this.focusNode, - required this.controller, + required this.controller, required this.isLight, }); + final Function() onTapPicker; final Currency selectedCurrency; final FocusNode? focusNode; final TextEditingController controller; + final bool isLight; @override Widget build(BuildContext context) { final arrowBottomPurple = Image.asset( 'assets/images/arrow_bottom_purple_icon.png', - color: Colors.white, + color: Theme.of(context).accentTextTheme.headline2!.backgroundColor!, height: 8, ); final _width = MediaQuery.of(context).size.width; @@ -38,14 +39,14 @@ class CurrencyInputField extends StatelessWidget { keyboardType: TextInputType.numberWithOptions(signed: false, decimal: true), inputFormatters: [FilteringTextInputFormatter.allow(RegExp(r'^\d+(\.|\,)?\d{0,8}'))], hintText: '0.000', - placeholderTextStyle: TextStyle( + placeholderTextStyle: isLight ? null : TextStyle( color: Theme.of(context).primaryTextTheme.headline5!.color!, fontWeight: FontWeight.w600, ), borderColor: Theme.of(context).accentTextTheme.headline6!.backgroundColor!, - textColor: Colors.white, + textColor: Theme.of(context).accentTextTheme.headline2!.backgroundColor!, textStyle: TextStyle( - color: Colors.white, + color: Theme.of(context).accentTextTheme.headline2!.backgroundColor!, ), prefixIcon: Padding( padding: EdgeInsets.only( @@ -68,7 +69,7 @@ class CurrencyInputField extends StatelessWidget { style: TextStyle( fontWeight: FontWeight.w600, fontSize: 16, - color: Colors.white, + color: Theme.of(context).accentTextTheme.headline2!.backgroundColor!, ), ), if (selectedCurrency.tag != null) @@ -103,7 +104,8 @@ class CurrencyInputField extends StatelessWidget { style: TextStyle( fontWeight: FontWeight.w600, fontSize: 20, - color: Colors.white, + color: + Theme.of(context).accentTextTheme.headline2!.backgroundColor!, ), ), ), diff --git a/lib/src/screens/receive/widgets/qr_image.dart b/lib/src/screens/receive/widgets/qr_image.dart index c624ad3c0..f4c1eb177 100644 --- a/lib/src/screens/receive/widgets/qr_image.dart +++ b/lib/src/screens/receive/widgets/qr_image.dart @@ -23,7 +23,7 @@ class QrImage extends StatelessWidget { size: size, foregroundColor: Colors.black, backgroundColor: Colors.white, - padding: EdgeInsets.zero, + padding: const EdgeInsets.all(8.0), ); } } diff --git a/lib/src/screens/receive/widgets/qr_widget.dart b/lib/src/screens/receive/widgets/qr_widget.dart index 33cc3d598..610b0d245 100644 --- a/lib/src/screens/receive/widgets/qr_widget.dart +++ b/lib/src/screens/receive/widgets/qr_widget.dart @@ -119,6 +119,7 @@ class QRWidget extends StatelessWidget { controller: amountController, onTapPicker: () => _presentPicker(context), selectedCurrency: addressListViewModel.selectedCurrency, + isLight: isLight, ), ), ), diff --git a/lib/src/screens/restore/restore_from_keys.dart b/lib/src/screens/restore/restore_from_keys.dart deleted file mode 100644 index cd0116e56..000000000 --- a/lib/src/screens/restore/restore_from_keys.dart +++ /dev/null @@ -1,83 +0,0 @@ -import 'package:flutter/services.dart'; -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -import 'package:cake_wallet/generated/i18n.dart'; -import 'package:cake_wallet/src/widgets/blockchain_height_widget.dart'; -import 'package:cake_wallet/src/widgets/base_text_form_field.dart'; - -class RestoreFromKeysFrom extends StatefulWidget { - @override - _RestoreFromKeysFromState createState() => _RestoreFromKeysFromState(); -} - -class _RestoreFromKeysFromState extends State { - final _formKey = GlobalKey(); - final _blockchainHeightKey = GlobalKey(); - final _nameController = TextEditingController(); - final _addressController = TextEditingController(); - final _viewKeyController = TextEditingController(); - final _spendKeyController = TextEditingController(); - final _wifController = TextEditingController(); - - @override - void initState() { - // _nameController.addListener(() => - // widget.walletRestorationFromKeysVM.name = _nameController.text); - // _addressController.addListener(() => - // widget.walletRestorationFromKeysVM.address = _addressController.text); - // _viewKeyController.addListener(() => - // widget.walletRestorationFromKeysVM.viewKey = _viewKeyController.text); - // _spendKeyController.addListener(() => - // widget.walletRestorationFromKeysVM.spendKey = _spendKeyController.text); - // _wifController.addListener(() => - // widget.walletRestorationFromKeysVM.wif = _wifController.text); - - super.initState(); - } - - @override - void dispose() { - _nameController.dispose(); - _addressController.dispose(); - _viewKeyController.dispose(); - _spendKeyController.dispose(); - _wifController.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return Container( - padding: EdgeInsets.only(left: 24, right: 24), - child: Form( - key: _formKey, - child: Column(children: [ - BaseTextFormField( - controller: _addressController, - keyboardType: TextInputType.multiline, - maxLines: null, - hintText: S.of(context).restore_address, - ), - Container( - padding: EdgeInsets.only(top: 20.0), - child: BaseTextFormField( - controller: _viewKeyController, - hintText: S.of(context).restore_view_key_private, - )), - Container( - padding: EdgeInsets.only(top: 20.0), - child: BaseTextFormField( - controller: _spendKeyController, - hintText: S.of(context).restore_spend_key_private, - )), - BlockchainHeightWidget( - key: _blockchainHeightKey, - onHeightChange: (height) { - // widget.walletRestorationFromKeysVM.height = height; - print(height); - }), - ]), - ), - ); - } -} diff --git a/lib/src/screens/send/widgets/send_card.dart b/lib/src/screens/send/widgets/send_card.dart index cdae9a8df..297e7d660 100644 --- a/lib/src/screens/send/widgets/send_card.dart +++ b/lib/src/screens/send/widgets/send_card.dart @@ -548,6 +548,10 @@ class SendCardState extends State } void _setEffects(BuildContext context) { + if (_effectsInstalled) { + return; + } + if (output.address.isNotEmpty) { addressController.text = output.address; } @@ -558,10 +562,6 @@ class SendCardState extends State noteController.text = output.note; extractedAddressController.text = output.extractedAddress; - if (_effectsInstalled) { - return; - } - cryptoAmountController.addListener(() { final amount = cryptoAmountController.text; diff --git a/lib/src/screens/settings/privacy_page.dart b/lib/src/screens/settings/privacy_page.dart index 81e2715f2..d21ae0ae8 100644 --- a/lib/src/screens/settings/privacy_page.dart +++ b/lib/src/screens/settings/privacy_page.dart @@ -50,11 +50,23 @@ class PrivacyPage extends BasePage { _privacySettingsViewModel.setShouldSaveRecipientAddress(value); }), if (Platform.isAndroid) + SettingsSwitcherCell( + title: S.current.prevent_screenshots, + value: _privacySettingsViewModel.isAppSecure, + onValueChange: (BuildContext _, bool value) { + _privacySettingsViewModel.setIsAppSecure(value); + }), SettingsSwitcherCell( - title: S.current.prevent_screenshots, - value: _privacySettingsViewModel.isAppSecure, + title: S.current.disable_buy, + value: _privacySettingsViewModel.disableBuy, onValueChange: (BuildContext _, bool value) { - _privacySettingsViewModel.setIsAppSecure(value); + _privacySettingsViewModel.setDisableBuy(value); + }), + SettingsSwitcherCell( + title: S.current.disable_sell, + value: _privacySettingsViewModel.disableSell, + onValueChange: (BuildContext _, bool value) { + _privacySettingsViewModel.setDisableSell(value); }), ], ); diff --git a/lib/src/screens/settings/security_backup_page.dart b/lib/src/screens/settings/security_backup_page.dart index 0933560f7..24c597a00 100644 --- a/lib/src/screens/settings/security_backup_page.dart +++ b/lib/src/screens/settings/security_backup_page.dart @@ -8,6 +8,7 @@ import 'package:cake_wallet/src/screens/settings/widgets/settings_cell_with_arro import 'package:cake_wallet/src/screens/settings/widgets/settings_picker_cell.dart'; import 'package:cake_wallet/src/screens/settings/widgets/settings_switcher_cell.dart'; import 'package:cake_wallet/src/widgets/standard_list.dart'; +import 'package:cake_wallet/utils/device_info.dart'; import 'package:cake_wallet/view_model/settings/security_settings_view_model.dart'; import 'package:flutter/material.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; @@ -48,29 +49,30 @@ class SecurityBackupPage extends BasePage { ), ), StandardListSeparator(padding: EdgeInsets.symmetric(horizontal: 24)), - Observer(builder: (_) { - return SettingsSwitcherCell( - title: S.current.settings_allow_biometrical_authentication, - value: _securitySettingsViewModel.allowBiometricalAuthentication, - onValueChange: (BuildContext context, bool value) { - if (value) { - _authService.authenticateAction(context, - onAuthSuccess: (isAuthenticatedSuccessfully) async { - if (isAuthenticatedSuccessfully) { - if (await _securitySettingsViewModel.biometricAuthenticated()) { + if (DeviceInfo.instance.isMobile) + Observer(builder: (_) { + return SettingsSwitcherCell( + title: S.current.settings_allow_biometrical_authentication, + value: _securitySettingsViewModel.allowBiometricalAuthentication, + onValueChange: (BuildContext context, bool value) { + if (value) { + _authService.authenticateAction(context, + onAuthSuccess: (isAuthenticatedSuccessfully) async { + if (isAuthenticatedSuccessfully) { + if (await _securitySettingsViewModel.biometricAuthenticated()) { + _securitySettingsViewModel + .setAllowBiometricalAuthentication(isAuthenticatedSuccessfully); + } + } else { _securitySettingsViewModel .setAllowBiometricalAuthentication(isAuthenticatedSuccessfully); } - } else { - _securitySettingsViewModel - .setAllowBiometricalAuthentication(isAuthenticatedSuccessfully); - } - }); - } else { - _securitySettingsViewModel.setAllowBiometricalAuthentication(value); - } - }); - }), + }); + } else { + _securitySettingsViewModel.setAllowBiometricalAuthentication(value); + } + }); + }), Observer(builder: (_) { return SettingsPickerCell( title: S.current.require_pin_after, diff --git a/lib/src/widgets/check_box_picker.dart b/lib/src/widgets/check_box_picker.dart index e874f587a..47b0be041 100644 --- a/lib/src/widgets/check_box_picker.dart +++ b/lib/src/widgets/check_box_picker.dart @@ -1,8 +1,7 @@ import 'package:cake_wallet/palette.dart'; import 'package:cake_wallet/utils/responsive_layout_util.dart'; import 'package:flutter/material.dart'; -import 'package:cake_wallet/src/widgets/alert_background.dart'; -import 'package:cake_wallet/src/widgets/alert_close_button.dart'; +import 'package:cake_wallet/src/widgets/picker_wrapper_widget.dart'; class CheckBoxPicker extends StatefulWidget { CheckBoxPicker({ @@ -32,73 +31,57 @@ class CheckBoxPickerState extends State { @override Widget build(BuildContext context) { - return AlertBackground( - child: Column( - children: [ - Expanded( - child: Stack( - alignment: Alignment.center, - children: [ - Column( + return PickerWrapperWidget( + children: [ + if (widget.title.isNotEmpty) + Container( + padding: EdgeInsets.symmetric(horizontal: 24), + child: Text( + widget.title, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 18, + fontFamily: 'Lato', + fontWeight: FontWeight.bold, + decoration: TextDecoration.none, + color: Colors.white, + ), + ), + ), + Padding( + padding: EdgeInsets.only(left: 24, right: 24, top: 24), + child: ClipRRect( + borderRadius: BorderRadius.all(Radius.circular(30)), + child: Container( + color: Theme.of(context).accentTextTheme.headline6!.color!, + child: ConstrainedBox( + constraints: BoxConstraints( + maxHeight: MediaQuery.of(context).size.height * 0.65, + maxWidth: ResponsiveLayoutUtil.kPopupWidth, + ), + child: Column( mainAxisSize: MainAxisSize.min, - children: [ - if (widget.title.isNotEmpty) - Container( - padding: EdgeInsets.symmetric(horizontal: 24), - child: Text( - widget.title, - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 18, - fontFamily: 'Lato', - fontWeight: FontWeight.bold, - decoration: TextDecoration.none, - color: Colors.white, - ), - ), - ), - Padding( - padding: EdgeInsets.only(left: 24, right: 24, top: 24), - child: ClipRRect( - borderRadius: BorderRadius.all(Radius.circular(30)), - child: Container( - color: Theme.of(context).accentTextTheme.headline6!.color!, - child: ConstrainedBox( - constraints: BoxConstraints( - maxHeight: MediaQuery.of(context).size.height * 0.65, - maxWidth: ResponsiveLayoutUtil.kPopupWidth, - ), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Flexible( - child: Stack( - alignment: Alignment.center, - children: [ - items.length > 3 - ? Scrollbar( - controller: controller, - child: itemsList(), - ) - : itemsList(), - ], - ), - ), - ], - ), - ), - ), + children: [ + Flexible( + child: Stack( + alignment: Alignment.center, + children: [ + items.length > 3 + ? Scrollbar( + controller: controller, + child: itemsList(), + ) + : itemsList(), + ], ), ), ], ), - SizedBox(height: ResponsiveLayoutUtil.kPopupSpaceHeight), - AlertCloseButton(), - ], + ), ), ), - ], - ), + ), + ], ); } @@ -111,7 +94,10 @@ class CheckBoxPickerState extends State { shrinkWrap: true, separatorBuilder: (context, index) => widget.isSeparated ? Divider( - color: Theme.of(context).accentTextTheme.headline6!.backgroundColor!, + color: Theme.of(context) + .accentTextTheme + .headline6! + .backgroundColor!, height: 1, ) : const SizedBox(), diff --git a/lib/src/widgets/picker.dart b/lib/src/widgets/picker.dart index 34ff10316..e160a083b 100644 --- a/lib/src/widgets/picker.dart +++ b/lib/src/widgets/picker.dart @@ -2,9 +2,8 @@ import 'package:cake_wallet/utils/responsive_layout_util.dart'; import 'package:flutter/material.dart'; -import 'package:cake_wallet/src/widgets/alert_background.dart'; -import 'package:cake_wallet/src/widgets/alert_close_button.dart'; import 'package:cw_core/currency.dart'; +import 'package:cake_wallet/src/widgets/picker_wrapper_widget.dart'; class Picker extends StatefulWidget { Picker({ @@ -114,171 +113,130 @@ class _PickerState extends State> { final mq = MediaQuery.of(context); final bottom = mq.viewInsets.bottom; final height = mq.size.height - bottom; - final screenCenter = height / 2; - double closeButtonBottom = 60; double containerHeight = height * 0.65; if (bottom > 0) { // increase a bit or it gets too squished in the top containerHeight = height * 0.75; - - final containerCenter = containerHeight / 2; - final containerBottom = screenCenter - containerCenter; - - final hasTitle = widget.title == null || widget.title!.isEmpty; - - // position the close button right below the search container - closeButtonBottom = closeButtonBottom - - containerBottom + - (hasTitle ? padding : padding / 1.5); } - return AlertBackground( - child: Column( - children: [ - Expanded( - flex: 1, - child: Stack( - alignment: Alignment.center, - children: [ - Column( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - if (widget.title?.isNotEmpty ?? false) - Container( - padding: EdgeInsets.symmetric(horizontal: padding), - child: Text( - widget.title!, - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 18, - fontFamily: 'Lato', - fontWeight: FontWeight.bold, - decoration: TextDecoration.none, - color: Colors.white, - ), - ), - ), - Padding( - padding: EdgeInsets.symmetric(horizontal: padding), - child: ClipRRect( - borderRadius: BorderRadius.all(Radius.circular(30)), - child: Container( - color: Theme.of(context) - .accentTextTheme - .headline6! - .color!, - child: ConstrainedBox( - constraints: BoxConstraints( - maxHeight: containerHeight, - maxWidth: ResponsiveLayoutUtil.kPopupWidth, - ), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - if (widget.hintText != null) - Padding( - padding: const EdgeInsets.all(16), - child: TextFormField( - controller: searchController, - style: TextStyle( - color: Theme.of(context) - .primaryTextTheme - .headline6! - .color!), - decoration: InputDecoration( - hintText: widget.hintText, - prefixIcon: Image.asset( - "assets/images/search_icon.png"), - filled: true, - fillColor: Theme.of(context) - .accentTextTheme - .headline3! - .color!, - alignLabelWithHint: false, - contentPadding: - const EdgeInsets.symmetric( - vertical: 4, horizontal: 16), - enabledBorder: OutlineInputBorder( - borderRadius: - BorderRadius.circular(14), - borderSide: const BorderSide( - color: Colors.transparent, - )), - focusedBorder: OutlineInputBorder( - borderRadius: - BorderRadius.circular(14), - borderSide: const BorderSide( - color: Colors.transparent, - )), - ), - ), - ), - Divider( - color: Theme.of(context) - .accentTextTheme - .headline6! - .backgroundColor!, - height: 1, - ), - if (widget.selectedAtIndex != -1) - buildSelectedItem(widget.selectedAtIndex), - Flexible( - child: Stack( - alignment: Alignment.center, - children: [ - filteredItems.length > 3 - ? Scrollbar( - controller: controller, - child: itemsList(), - ) - : itemsList(), - (widget.description?.isNotEmpty ?? false) - ? Positioned( - bottom: padding, - left: padding, - right: padding, - child: Text( - widget.description!, - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w500, - fontFamily: 'Lato', - decoration: - TextDecoration.none, - color: Theme.of(context) - .primaryTextTheme - .headline6! - .color!, - ), - ), - ) - : Offstage(), - ], - ), - ), - ], - ), - ), - ), - ), - ) - ], - ), - SizedBox(height: ResponsiveLayoutUtil.kPopupSpaceHeight), - AlertCloseButton(bottom: closeButtonBottom), - ], + return PickerWrapperWidget( + hasTitle: widget.title?.isNotEmpty ?? false, + children: [ + if (widget.title?.isNotEmpty ?? false) + Container( + padding: EdgeInsets.symmetric(horizontal: padding), + child: Text( + widget.title!, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 18, + fontFamily: 'Lato', + fontWeight: FontWeight.bold, + decoration: TextDecoration.none, + color: Colors.white, + ), ), ), - // gives the extra spacing using MediaQuery.viewInsets.bottom - // to simulate a keyboard area - SizedBox( - height: bottom, - ) - ], - ), + Padding( + padding: EdgeInsets.symmetric(horizontal: padding), + child: ClipRRect( + borderRadius: BorderRadius.all(Radius.circular(30)), + child: Container( + color: Theme.of(context).accentTextTheme.headline6!.color!, + child: ConstrainedBox( + constraints: BoxConstraints( + maxHeight: containerHeight, + maxWidth: ResponsiveLayoutUtil.kPopupWidth, + ), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + if (widget.hintText != null) + Padding( + padding: const EdgeInsets.all(16), + child: TextFormField( + controller: searchController, + style: TextStyle( + color: Theme.of(context) + .primaryTextTheme + .headline6! + .color!), + decoration: InputDecoration( + hintText: widget.hintText, + prefixIcon: + Image.asset("assets/images/search_icon.png"), + filled: true, + fillColor: Theme.of(context) + .accentTextTheme + .headline3! + .color!, + alignLabelWithHint: false, + contentPadding: const EdgeInsets.symmetric( + vertical: 4, horizontal: 16), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(14), + borderSide: const BorderSide( + color: Colors.transparent, + )), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(14), + borderSide: const BorderSide( + color: Colors.transparent, + )), + ), + ), + ), + Divider( + color: Theme.of(context) + .accentTextTheme + .headline6! + .backgroundColor!, + height: 1, + ), + if (widget.selectedAtIndex != -1) + buildSelectedItem(widget.selectedAtIndex), + Flexible( + child: Stack( + alignment: Alignment.center, + children: [ + filteredItems.length > 3 + ? Scrollbar( + controller: controller, + child: itemsList(), + ) + : itemsList(), + (widget.description?.isNotEmpty ?? false) + ? Positioned( + bottom: padding, + left: padding, + right: padding, + child: Text( + widget.description!, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w500, + fontFamily: 'Lato', + decoration: TextDecoration.none, + color: Theme.of(context) + .primaryTextTheme + .headline6! + .color!, + ), + ), + ) + : Offstage(), + ], + ), + ), + ], + ), + ), + ), + ), + ) + ], ); } diff --git a/lib/src/widgets/picker_wrapper_widget.dart b/lib/src/widgets/picker_wrapper_widget.dart new file mode 100644 index 000000000..244199936 --- /dev/null +++ b/lib/src/widgets/picker_wrapper_widget.dart @@ -0,0 +1,61 @@ +import 'package:cake_wallet/utils/responsive_layout_util.dart'; +import 'package:flutter/material.dart'; +import 'package:cake_wallet/src/widgets/alert_background.dart'; +import 'package:cake_wallet/src/widgets/alert_close_button.dart'; + +class PickerWrapperWidget extends StatelessWidget { + PickerWrapperWidget({required this.children, this.hasTitle = false}); + + final List children; + final bool hasTitle; + + @override + Widget build(BuildContext context) { + final double padding = 24; + + final mq = MediaQuery.of(context); + final bottom = mq.viewInsets.bottom; + final height = mq.size.height - bottom; + final screenCenter = height / 2; + + double closeButtonBottom = 60; + double containerHeight = height * 0.65; + if (bottom > 0) { + // increase a bit or it gets too squished in the top + containerHeight = height * 0.75; + + final containerCenter = containerHeight / 2; + final containerBottom = screenCenter - containerCenter; + + // position the close button right below the search container + closeButtonBottom = closeButtonBottom - + containerBottom + (!hasTitle ? padding : padding / 1.5); + } + + return AlertBackground( + child: Column( + children: [ + Expanded( + flex: 1, + child: Stack( + alignment: Alignment.center, + children: [ + Column( + mainAxisSize: MainAxisSize.min, + children: children, + ), + SizedBox(height: ResponsiveLayoutUtil.kPopupSpaceHeight), + AlertCloseButton(bottom: closeButtonBottom), + ], + ), + ), + // gives the extra spacing using MediaQuery.viewInsets.bottom + // to simulate a keyboard area + SizedBox( + height: bottom, + ) + ], + ), + ); + } +} diff --git a/lib/store/settings_store.dart b/lib/store/settings_store.dart index 400f7ac88..7ec784d93 100644 --- a/lib/store/settings_store.dart +++ b/lib/store/settings_store.dart @@ -34,6 +34,8 @@ abstract class SettingsStoreBase with Store { required BalanceDisplayMode initialBalanceDisplayMode, required bool initialSaveRecipientAddress, required bool initialAppSecure, + required bool initialDisableBuy, + required bool initialDisableSell, required FiatApiMode initialFiatMode, required bool initialAllowBiometricalAuthentication, required ExchangeApiMode initialExchangeStatus, @@ -57,6 +59,8 @@ abstract class SettingsStoreBase with Store { balanceDisplayMode = initialBalanceDisplayMode, shouldSaveRecipientAddress = initialSaveRecipientAddress, isAppSecure = initialAppSecure, + disableBuy = initialDisableBuy, + disableSell = initialDisableSell, fiatApiMode = initialFiatMode, allowBiometricalAuthentication = initialAllowBiometricalAuthentication, shouldShowMarketPlaceInDashboard = initialShouldShowMarketPlaceInDashboard, @@ -130,6 +134,16 @@ abstract class SettingsStoreBase with Store { } }); + reaction( + (_) => disableBuy, + (bool disableBuy) => sharedPreferences.setBool( + PreferencesKey.disableBuyKey, disableBuy)); + + reaction( + (_) => disableSell, + (bool disableSell) => sharedPreferences.setBool( + PreferencesKey.disableSellKey, disableSell)); + if (Platform.isAndroid) { setIsAppSecureNative(isAppSecure); } @@ -217,6 +231,12 @@ abstract class SettingsStoreBase with Store { @observable bool isAppSecure; + @observable + bool disableBuy; + + @observable + bool disableSell; + @observable bool allowBiometricalAuthentication; @@ -309,6 +329,10 @@ abstract class SettingsStoreBase with Store { sharedPreferences.getBool(PreferencesKey.shouldSaveRecipientAddressKey) ?? false; final isAppSecure = sharedPreferences.getBool(PreferencesKey.isAppSecureKey) ?? false; + final disableBuy = + sharedPreferences.getBool(PreferencesKey.disableBuyKey) ?? false; + final disableSell = + sharedPreferences.getBool(PreferencesKey.disableSellKey) ?? false; final currentFiatApiMode = FiatApiMode.deserialize( raw: sharedPreferences .getInt(PreferencesKey.currentFiatApiModeKey) ?? FiatApiMode.enabled.raw); @@ -388,6 +412,8 @@ abstract class SettingsStoreBase with Store { initialBalanceDisplayMode: currentBalanceDisplayMode, initialSaveRecipientAddress: shouldSaveRecipientAddress, initialAppSecure: isAppSecure, + initialDisableBuy: disableBuy, + initialDisableSell: disableSell, initialFiatMode: currentFiatApiMode, initialAllowBiometricalAuthentication: allowBiometricalAuthentication, initialExchangeStatus: exchangeStatus, @@ -435,6 +461,10 @@ abstract class SettingsStoreBase with Store { sharedPreferences.getBool(PreferencesKey.shouldSaveRecipientAddressKey) ?? shouldSaveRecipientAddress; isAppSecure = sharedPreferences.getBool(PreferencesKey.isAppSecureKey) ?? isAppSecure; + disableBuy = + sharedPreferences.getBool(PreferencesKey.disableBuyKey) ?? disableBuy; + disableSell = + sharedPreferences.getBool(PreferencesKey.disableSellKey) ?? disableSell; allowBiometricalAuthentication = sharedPreferences .getBool(PreferencesKey.allowBiometricalAuthenticationKey) ?? allowBiometricalAuthentication; diff --git a/lib/utils/exception_handler.dart b/lib/utils/exception_handler.dart index d3689e7e0..4b156ade1 100644 --- a/lib/utils/exception_handler.dart +++ b/lib/utils/exception_handler.dart @@ -141,9 +141,9 @@ class ExceptionHandler { "errno = 103", // SocketException: Software caused connection abort "errno = 104", // SocketException: Connection reset by peer "errno = 110", // SocketException: Connection timed out - "HttpException: Connection reset by peer", - "HttpException: Connection closed before full header was received", - "HandshakeException: Connection terminated during handshake", + "Connection reset by peer", + "Connection closed before full header was received", + "Connection terminated during handshake", "PERMISSION_NOT_GRANTED", ]; @@ -172,7 +172,7 @@ class ExceptionHandler { } await file.writeAsString( - "App Version: $currentVersion\n\nDevice Info $deviceInfo", + "App Version: $currentVersion\n\nDevice Info $deviceInfo\n\n", mode: FileMode.append, ); } @@ -193,6 +193,7 @@ class ExceptionHandler { 'systemVersion': data.systemVersion, 'model': data.model, 'localizedModel': data.localizedModel, + 'isPhysicalDevice': data.isPhysicalDevice, }; } diff --git a/lib/view_model/dashboard/dashboard_view_model.dart b/lib/view_model/dashboard/dashboard_view_model.dart index c28603a51..cfb72cb9e 100644 --- a/lib/view_model/dashboard/dashboard_view_model.dart +++ b/lib/view_model/dashboard/dashboard_view_model.dart @@ -46,9 +46,7 @@ abstract class DashboardViewModelBase with Store { required this.anonpayTransactionsStore}) : isOutdatedElectrumWallet = false, hasSellAction = false, - isEnabledSellAction = false, hasBuyAction = false, - isEnabledBuyAction = false, hasExchangeAction = false, isShowFirstYatIntroduction = false, isShowSecondYatIntroduction = false, @@ -287,14 +285,19 @@ abstract class DashboardViewModelBase with Store { @observable bool hasExchangeAction; - @observable - bool isEnabledBuyAction; + @computed + bool get isEnabledBuyAction => + !settingsStore.disableBuy && wallet.type != WalletType.haven; @observable bool hasBuyAction; - @observable - bool isEnabledSellAction; + @computed + bool get isEnabledSellAction => + !settingsStore.disableSell && + wallet.type != WalletType.haven && + wallet.type != WalletType.monero && + wallet.type != WalletType.litecoin; @observable bool hasSellAction; @@ -398,11 +401,7 @@ abstract class DashboardViewModelBase with Store { void updateActions() { hasExchangeAction = !isHaven; - isEnabledBuyAction = wallet.type != WalletType.haven; hasBuyAction = !isHaven; - isEnabledSellAction = wallet.type != WalletType.haven - && wallet.type != WalletType.monero - && wallet.type != WalletType.litecoin; hasSellAction = !isHaven; } } diff --git a/lib/view_model/exchange/exchange_trade_view_model.dart b/lib/view_model/exchange/exchange_trade_view_model.dart index 194dc9d45..1d9f4f582 100644 --- a/lib/view_model/exchange/exchange_trade_view_model.dart +++ b/lib/view_model/exchange/exchange_trade_view_model.dart @@ -123,8 +123,8 @@ abstract class ExchangeTradeViewModelBase with Store { } void _updateItems() { - final tagFrom = trade.from.tag != null ? '${trade.from.tag}' + ' ' : ''; - final tagTo = trade.to.tag != null ? '${trade.to.tag}' + ' ' : ''; + final tagFrom = tradesStore.trade!.from.tag != null ? '${tradesStore.trade!.from.tag}' + ' ' : ''; + final tagTo = tradesStore.trade!.to.tag != null ? '${tradesStore.trade!.to.tag}' + ' ' : ''; items.clear(); items.add(ExchangeTradeItem( title: "${trade.provider.title} ${S.current.id}", data: '${trade.id}', isCopied: true)); @@ -142,11 +142,11 @@ abstract class ExchangeTradeViewModelBase with Store { items.addAll([ ExchangeTradeItem(title: S.current.amount, data: '${trade.amount}', isCopied: true), ExchangeTradeItem( - title: S.current.send_to_this_address('${trade.from}', tagFrom) + ':', + title: S.current.send_to_this_address('${tradesStore.trade!.from}', tagFrom) + ':', data: trade.inputAddress ?? '', isCopied: true), ExchangeTradeItem( - title: S.current.arrive_in_this_address('${trade.to}', tagTo) + ':', + title: S.current.arrive_in_this_address('${tradesStore.trade!.to}', tagTo) + ':', data: trade.payoutAddress ?? '', isCopied: true), ]); diff --git a/lib/view_model/exchange/exchange_view_model.dart b/lib/view_model/exchange/exchange_view_model.dart index 3c016d4b0..77fb9a3d4 100644 --- a/lib/view_model/exchange/exchange_view_model.dart +++ b/lib/view_model/exchange/exchange_view_model.dart @@ -3,7 +3,6 @@ import 'dart:collection'; import 'dart:convert'; import 'package:cake_wallet/entities/exchange_api_mode.dart'; -import 'package:cake_wallet/entities/fiat_api_mode.dart'; import 'package:cake_wallet/entities/preferences_key.dart'; import 'package:cake_wallet/exchange/sideshift/sideshift_exchange_provider.dart'; import 'package:cake_wallet/exchange/sideshift/sideshift_request.dart'; @@ -244,7 +243,7 @@ abstract class ExchangeViewModelBase with Store { List depositCurrencies; - NumberFormat _cryptoNumberFormat; + final NumberFormat _cryptoNumberFormat; final SettingsStore _settingsStore; @@ -388,27 +387,36 @@ abstract class ExchangeViewModelBase with Store { double? lowestMin = double.maxFinite; double? highestMax = 0.0; - for (var provider in selectedProviders) { - /// if this provider is not valid for the current pair, skip it - if (!providersForCurrentPair().contains(provider)) { - continue; - } - - try { - final tempLimits = await provider.fetchLimits( - from: from, - to: to, - isFixedRateMode: isFixedRateMode); - - if (lowestMin != null && (tempLimits.min ?? -1) < lowestMin) { - lowestMin = tempLimits.min; + try { + for (var provider in selectedProviders) { + /// if this provider is not valid for the current pair, skip it + if (!providersForCurrentPair().contains(provider)) { + continue; } - if (highestMax != null && (tempLimits.max ?? double.maxFinite) > highestMax) { - highestMax = tempLimits.max; + + try { + final tempLimits = await provider.fetchLimits( + from: from, + to: to, + isFixedRateMode: isFixedRateMode); + + if (lowestMin != null && (tempLimits.min ?? -1) < lowestMin) { + lowestMin = tempLimits.min; + } + if (highestMax != null && (tempLimits.max ?? double.maxFinite) > highestMax) { + highestMax = tempLimits.max; + } + } catch (e) { + continue; } - } catch (e) { - continue; } + } on ConcurrentModificationError { + /// if user changed the selected providers while fetching limits + /// then delay the fetching limits a bit and try again + /// + /// this is because the limitation of collections that + /// you can't modify it while iterating through it + Future.delayed(Duration(milliseconds: 200), loadLimits); } if (lowestMin != double.maxFinite) { @@ -534,7 +542,7 @@ abstract class ExchangeViewModelBase with Store { /// /// this is because the limitation of the SplayTreeMap that /// you can't modify it while iterating through it - Future.delayed(Duration(milliseconds: 500), createTrade); + Future.delayed(Duration(milliseconds: 200), createTrade); } } diff --git a/lib/view_model/settings/privacy_settings_view_model.dart b/lib/view_model/settings/privacy_settings_view_model.dart index 91ddd2f34..912864e4a 100644 --- a/lib/view_model/settings/privacy_settings_view_model.dart +++ b/lib/view_model/settings/privacy_settings_view_model.dart @@ -24,6 +24,12 @@ abstract class PrivacySettingsViewModelBase with Store { @computed bool get isAppSecure => _settingsStore.isAppSecure; + @computed + bool get disableBuy => _settingsStore.disableBuy; + + @computed + bool get disableSell => _settingsStore.disableSell; + @action void setShouldSaveRecipientAddress(bool value) => _settingsStore.shouldSaveRecipientAddress = value; @@ -36,4 +42,10 @@ abstract class PrivacySettingsViewModelBase with Store { @action void setIsAppSecure(bool value) => _settingsStore.isAppSecure = value; + @action + void setDisableBuy(bool value) => _settingsStore.disableBuy = value; + + @action + void setDisableSell(bool value) => _settingsStore.disableSell = value; + } diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 437237153..c7f78b30f 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -25,7 +25,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { CwMoneroPlugin.register(with: registry.registrar(forPlugin: "CwMoneroPlugin")) DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin")) DevicelocalePlugin.register(with: registry.registrar(forPlugin: "DevicelocalePlugin")) - FlutterSecureStorageMacosPlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStorageMacosPlugin")) + FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin")) InAppReviewPlugin.register(with: registry.registrar(forPlugin: "InAppReviewPlugin")) FLTPackageInfoPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) diff --git a/macos/Podfile.lock b/macos/Podfile.lock index ea15b9495..3564ab712 100644 --- a/macos/Podfile.lock +++ b/macos/Podfile.lock @@ -23,7 +23,7 @@ PODS: - FlutterMacOS - devicelocale (0.0.1): - FlutterMacOS - - flutter_secure_storage_macos (3.3.1): + - flutter_secure_storage_macos (6.1.1): - FlutterMacOS - FlutterMacOS (1.0.0) - in_app_review (0.2.0): @@ -106,16 +106,16 @@ SPEC CHECKSUMS: cw_monero: ec03de55a19c4a2b174ea687e0f4202edc716fa4 device_info_plus: 5401765fde0b8d062a2f8eb65510fb17e77cf07f devicelocale: 9f0f36ac651cabae2c33f32dcff4f32b61c38225 - flutter_secure_storage_macos: 6ceee8fbc7f484553ad17f79361b556259df89aa + flutter_secure_storage_macos: d56e2d218c1130b262bef8b4a7d64f88d7f9c9ea FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 in_app_review: a850789fad746e89bce03d4aeee8078b45a53fd0 package_info: 6eba2fd8d3371dda2d85c8db6fe97488f24b74b2 - path_provider_foundation: c68054786f1b4f3343858c1e1d0caaded73f0be9 + path_provider_foundation: eaf5b3e458fc0e5fbb9940fb09980e853fe058b8 platform_device_id: 3e414428f45df149bbbfb623e2c0ca27c545b763 platform_device_id_macos: f763bb55f088be804d61b96eb4710b8ab6598e94 Reachability: 33e18b67625424e47b6cde6d202dce689ad7af96 share_plus_macos: 853ee48e7dce06b633998ca0735d482dd671ade4 - shared_preferences_foundation: 986fc17f3d3251412d18b0265f9c64113a8c2472 + shared_preferences_foundation: e2dae3258e06f44cc55f49d42024fd8dd03c590c url_launcher_macos: 5335912b679c073563f29d89d33d10d459f95451 wakelock_macos: bc3f2a9bd8d2e6c89fee1e1822e7ddac3bd004a9 diff --git a/pubspec_base.yaml b/pubspec_base.yaml index 0d6aa36e8..8c25bd80c 100644 --- a/pubspec_base.yaml +++ b/pubspec_base.yaml @@ -17,8 +17,8 @@ dependencies: git: url: https://github.com/cake-tech/flutter_secure_storage.git path: flutter_secure_storage - ref: cake-6.0.0 - version: 6.0.0 + ref: cake-8.0.0 + version: 8.0.0 # provider: ^6.0.3 rxdart: ^0.27.4 yaml: ^3.1.1 @@ -81,6 +81,10 @@ dependencies: path_provider_android: 2.0.24 shared_preferences_android: 2.0.17 url_launcher_android: 6.0.24 + bitcoin_flutter: + git: + url: https://github.com/cake-tech/bitcoin_flutter.git + ref: cake-update-v2 dev_dependencies: flutter_test: diff --git a/res/values/strings_ar.arb b/res/values/strings_ar.arb index 0caa02094..c2706a4d7 100644 --- a/res/values/strings_ar.arb +++ b/res/values/strings_ar.arb @@ -706,5 +706,7 @@ "error_text_input_below_minimum_limit":" المبلغ أقل من الحد الأدنى", "error_text_input_above_maximum_limit":"المبلغ أكبر من الحد الأقصى", "show_market_place": "إظهار السوق", - "prevent_screenshots": "منع لقطات الشاشة وتسجيل الشاشة" + "prevent_screenshots": "منع لقطات الشاشة وتسجيل الشاشة", + "disable_buy": "تعطيل إجراء الشراء", + "disable_sell": "قم بتعطيل إجراء البيع" } diff --git a/res/values/strings_bg.arb b/res/values/strings_bg.arb index 66a211ed9..3ab4732d6 100644 --- a/res/values/strings_bg.arb +++ b/res/values/strings_bg.arb @@ -702,5 +702,7 @@ "error_text_input_below_minimum_limit" : "Сумата е по-малко от минималната", "error_text_input_above_maximum_limit" : "Сумата надвишава максималната", "show_market_place":"Покажи пазар", - "prevent_screenshots": "Предотвратете екранни снимки и запис на екрана" + "prevent_screenshots": "Предотвратете екранни снимки и запис на екрана", + "disable_buy": "Деактивирайте действието за покупка", + "disable_sell": "Деактивирайте действието за продажба" } diff --git a/res/values/strings_cs.arb b/res/values/strings_cs.arb index 381e7c18e..9cb449265 100644 --- a/res/values/strings_cs.arb +++ b/res/values/strings_cs.arb @@ -702,5 +702,7 @@ "error_text_input_below_minimum_limit" : "Částka je menší než minimální hodnota", "error_text_input_above_maximum_limit" : "Částka je větší než maximální hodnota", "show_market_place": "Zobrazit trh", - "prevent_screenshots": "Zabránit vytváření snímků obrazovky a nahrávání obrazovky" + "prevent_screenshots": "Zabránit vytváření snímků obrazovky a nahrávání obrazovky", + "disable_buy": "Zakázat akci nákupu", + "disable_sell": "Zakázat akci prodeje" } diff --git a/res/values/strings_de.arb b/res/values/strings_de.arb index 930ed4bac..3a64b2029 100644 --- a/res/values/strings_de.arb +++ b/res/values/strings_de.arb @@ -708,5 +708,7 @@ "error_text_input_below_minimum_limit" : "Menge ist unter dem Minimum", "error_text_input_above_maximum_limit" : "Menge ist über dem Maximum", "show_market_place": "Marktplatz anzeigen", - "prevent_screenshots": "Verhindern Sie Screenshots und Bildschirmaufzeichnungen" + "prevent_screenshots": "Verhindern Sie Screenshots und Bildschirmaufzeichnungen", + "disable_buy": "Kaufaktion deaktivieren", + "disable_sell": "Verkaufsaktion deaktivieren" } diff --git a/res/values/strings_en.arb b/res/values/strings_en.arb index 22cc9b950..7a0bfd975 100644 --- a/res/values/strings_en.arb +++ b/res/values/strings_en.arb @@ -708,5 +708,7 @@ "error_text_input_below_minimum_limit" : "Amount is less than the minimum", "error_text_input_above_maximum_limit" : "Amount is more than the maximum", "show_market_place" :"Show Marketplace", - "prevent_screenshots": "Prevent screenshots and screen recording" + "prevent_screenshots": "Prevent screenshots and screen recording", + "disable_buy": "Disable buy action", + "disable_sell": "Disable sell action" } diff --git a/res/values/strings_es.arb b/res/values/strings_es.arb index 9159d7a12..edde8c15e 100644 --- a/res/values/strings_es.arb +++ b/res/values/strings_es.arb @@ -708,5 +708,7 @@ "error_text_input_below_minimum_limit" : "La cantidad es menos que mínima", "error_text_input_above_maximum_limit" : "La cantidad es más que el máximo", "show_market_place": "Mostrar mercado", - "prevent_screenshots": "Evitar capturas de pantalla y grabación de pantalla" + "prevent_screenshots": "Evitar capturas de pantalla y grabación de pantalla", + "disable_buy": "Desactivar acción de compra", + "disable_sell": "Desactivar acción de venta" } diff --git a/res/values/strings_fr.arb b/res/values/strings_fr.arb index 983fc5f46..7ef121ea3 100644 --- a/res/values/strings_fr.arb +++ b/res/values/strings_fr.arb @@ -708,5 +708,7 @@ "error_text_input_below_minimum_limit" : "Le montant est inférieur au minimum", "error_text_input_above_maximum_limit" : "Le montant est supérieur au maximum", "show_market_place" :"Afficher la place de marché", - "prevent_screenshots": "Empêcher les captures d'écran et l'enregistrement d'écran" + "prevent_screenshots": "Empêcher les captures d'écran et l'enregistrement d'écran", + "disable_buy": "Désactiver l'action d'achat", + "disable_sell": "Désactiver l'action de vente" } diff --git a/res/values/strings_hi.arb b/res/values/strings_hi.arb index 8b505cff2..f5292dfd0 100644 --- a/res/values/strings_hi.arb +++ b/res/values/strings_hi.arb @@ -708,5 +708,7 @@ "error_text_input_below_minimum_limit" : "राशि न्यूनतम से कम है", "error_text_input_above_maximum_limit" : "राशि अधिकतम से अधिक है", "show_market_place":"बाज़ार दिखाएँ", - "prevent_screenshots": "स्क्रीनशॉट और स्क्रीन रिकॉर्डिंग रोकें" + "prevent_screenshots": "स्क्रीनशॉट और स्क्रीन रिकॉर्डिंग रोकें", + "disable_buy": "खरीद कार्रवाई अक्षम करें", + "disable_sell": "बेचने की कार्रवाई अक्षम करें" } diff --git a/res/values/strings_hr.arb b/res/values/strings_hr.arb index f9b5fd0f2..d0fb4cc7a 100644 --- a/res/values/strings_hr.arb +++ b/res/values/strings_hr.arb @@ -708,5 +708,7 @@ "error_text_input_below_minimum_limit" : "Iznos je manji od minimalnog", "error_text_input_above_maximum_limit" : "Iznos je veći od maskimalnog", "show_market_place" : "Prikaži tržište", - "prevent_screenshots": "Spriječite snimke zaslona i snimanje zaslona" + "prevent_screenshots": "Spriječite snimke zaslona i snimanje zaslona", + "disable_buy": "Onemogući kupnju", + "disable_sell": "Onemogući akciju prodaje" } diff --git a/res/values/strings_id.arb b/res/values/strings_id.arb index b4dd13356..121650b9c 100644 --- a/res/values/strings_id.arb +++ b/res/values/strings_id.arb @@ -684,5 +684,7 @@ "error_text_input_below_minimum_limit" : "Jumlah kurang dari minimal", "error_text_input_above_maximum_limit" : "Jumlah lebih dari maksimal", "show_market_place": "Tampilkan Pasar", - "prevent_screenshots": "Cegah tangkapan layar dan perekaman layar" + "prevent_screenshots": "Cegah tangkapan layar dan perekaman layar", + "disable_buy": "Nonaktifkan tindakan beli", + "disable_sell": "Nonaktifkan aksi jual" } diff --git a/res/values/strings_it.arb b/res/values/strings_it.arb index 95ce251bd..f327310b2 100644 --- a/res/values/strings_it.arb +++ b/res/values/strings_it.arb @@ -708,5 +708,7 @@ "error_text_input_below_minimum_limit" : "L'ammontare è inferiore al minimo", "error_text_input_above_maximum_limit" : "L'ammontare è superiore al massimo", "show_market_place":"Mostra mercato", - "prevent_screenshots": "Impedisci screenshot e registrazione dello schermo" + "prevent_screenshots": "Impedisci screenshot e registrazione dello schermo", + "disable_buy": "Disabilita l'azione di acquisto", + "disable_sell": "Disabilita l'azione di vendita" } diff --git a/res/values/strings_ja.arb b/res/values/strings_ja.arb index c1b78cc64..f13e84407 100644 --- a/res/values/strings_ja.arb +++ b/res/values/strings_ja.arb @@ -708,5 +708,7 @@ "error_text_input_below_minimum_limit" : "金額は最小額より少ない", "error_text_input_above_maximum_limit" : "金額は最大値を超えています", "show_market_place":"マーケットプレイスを表示", - "prevent_screenshots": "スクリーンショットと画面録画を防止する" + "prevent_screenshots": "スクリーンショットと画面録画を防止する", + "disable_buy": "購入アクションを無効にする", + "disable_sell": "販売アクションを無効にする" } diff --git a/res/values/strings_ko.arb b/res/values/strings_ko.arb index f2e03ed55..f38f9e0c2 100644 --- a/res/values/strings_ko.arb +++ b/res/values/strings_ko.arb @@ -708,5 +708,7 @@ "error_text_input_below_minimum_limit" : "금액이 최소보다 적습니다.", "error_text_input_above_maximum_limit" : "금액이 최대 값보다 많습니다.", "show_market_place":"마켓플레이스 표시", - "prevent_screenshots": "스크린샷 및 화면 녹화 방지" + "prevent_screenshots": "스크린샷 및 화면 녹화 방지", + "disable_buy": "구매 행동 비활성화", + "disable_sell": "판매 조치 비활성화" } diff --git a/res/values/strings_my.arb b/res/values/strings_my.arb index c314dfb65..73e282b7c 100644 --- a/res/values/strings_my.arb +++ b/res/values/strings_my.arb @@ -708,5 +708,7 @@ "error_text_input_below_minimum_limit" : "ပမာဏသည် အနိမ့်ဆုံးထက်နည်းသည်။", "error_text_input_above_maximum_limit" : "ပမာဏသည် အများဆုံးထက် ပိုများသည်။", "show_market_place":"စျေးကွက်ကိုပြသပါ။", - "prevent_screenshots": "ဖန်သားပြင်ဓာတ်ပုံများနှင့် မျက်နှာပြင်ရိုက်ကူးခြင်းကို တားဆီးပါ။" + "prevent_screenshots": "ဖန်သားပြင်ဓာတ်ပုံများနှင့် မျက်နှာပြင်ရိုက်ကူးခြင်းကို တားဆီးပါ။", + "disable_buy": "ဝယ်ယူမှု လုပ်ဆောင်ချက်ကို ပိတ်ပါ။", + "disable_sell": "ရောင်းချခြင်းလုပ်ဆောင်ချက်ကို ပိတ်ပါ။" } diff --git a/res/values/strings_nl.arb b/res/values/strings_nl.arb index 734630c96..a25a536cf 100644 --- a/res/values/strings_nl.arb +++ b/res/values/strings_nl.arb @@ -708,5 +708,7 @@ "error_text_input_below_minimum_limit" : "Bedrag is minder dan minimaal", "error_text_input_above_maximum_limit" : "Bedrag is meer dan maximaal", "show_market_place":"Toon Marktplaats", - "prevent_screenshots": "Voorkom screenshots en schermopname" + "prevent_screenshots": "Voorkom screenshots en schermopname", + "disable_buy": "Koopactie uitschakelen", + "disable_sell": "Verkoopactie uitschakelen" } diff --git a/res/values/strings_pl.arb b/res/values/strings_pl.arb index 23b7bbb61..a35d90429 100644 --- a/res/values/strings_pl.arb +++ b/res/values/strings_pl.arb @@ -708,5 +708,7 @@ "error_text_input_below_minimum_limit" : "Kwota jest mniejsza niż minimalna", "error_text_input_above_maximum_limit" : "Kwota jest większa niż maksymalna", "show_market_place" : "Pokaż rynek", - "prevent_screenshots": "Zapobiegaj zrzutom ekranu i nagrywaniu ekranu" + "prevent_screenshots": "Zapobiegaj zrzutom ekranu i nagrywaniu ekranu", + "disable_buy": "Wyłącz akcję kupna", + "disable_sell": "Wyłącz akcję sprzedaży" } diff --git a/res/values/strings_pt.arb b/res/values/strings_pt.arb index 0346b1ff4..69dc9437f 100644 --- a/res/values/strings_pt.arb +++ b/res/values/strings_pt.arb @@ -707,5 +707,7 @@ "error_text_input_below_minimum_limit" : "O valor é menor que o mínimo", "error_text_input_above_maximum_limit" : "O valor é superior ao máximo", "show_market_place":"Mostrar mercado", - "prevent_screenshots": "Evite capturas de tela e gravação de tela" + "prevent_screenshots": "Evite capturas de tela e gravação de tela", + "disable_buy": "Desativar ação de compra", + "disable_sell": "Desativar ação de venda" } diff --git a/res/values/strings_ru.arb b/res/values/strings_ru.arb index 33058a13e..67caa1776 100644 --- a/res/values/strings_ru.arb +++ b/res/values/strings_ru.arb @@ -708,5 +708,7 @@ "error_text_input_below_minimum_limit" : "Сумма меньше минимальной", "error_text_input_above_maximum_limit" : "Сумма больше максимальной", "show_market_place":"Показать торговую площадку", - "prevent_screenshots": "Предотвратить скриншоты и запись экрана" + "prevent_screenshots": "Предотвратить скриншоты и запись экрана", + "disable_buy": "Отключить действие покупки", + "disable_sell": "Отключить действие продажи" } diff --git a/res/values/strings_th.arb b/res/values/strings_th.arb index f1cc320ac..02e60abaf 100644 --- a/res/values/strings_th.arb +++ b/res/values/strings_th.arb @@ -706,5 +706,7 @@ "error_text_input_below_minimum_limit" : "จำนวนเงินน้อยกว่าขั้นต่ำ", "error_text_input_above_maximum_limit" : "จำนวนเงินสูงกว่าค่าสูงสุด", "show_market_place":"แสดงตลาดกลาง", - "prevent_screenshots": "ป้องกันภาพหน้าจอและการบันทึกหน้าจอ" + "prevent_screenshots": "ป้องกันภาพหน้าจอและการบันทึกหน้าจอ", + "disable_buy": "ปิดการใช้งานการซื้อ", + "disable_sell": "ปิดการใช้งานการขาย" } diff --git a/res/values/strings_tr.arb b/res/values/strings_tr.arb index 675fbb5aa..30524d4d7 100644 --- a/res/values/strings_tr.arb +++ b/res/values/strings_tr.arb @@ -708,5 +708,7 @@ "error_text_input_below_minimum_limit" : "Miktar minimumdan daha azdır", "error_text_input_above_maximum_limit" : "Miktar maksimumdan daha fazla", "show_market_place":"Pazar Yerini Göster", - "prevent_screenshots": "Ekran görüntülerini ve ekran kaydını önleyin" + "prevent_screenshots": "Ekran görüntülerini ve ekran kaydını önleyin", + "disable_buy": "Satın alma işlemini devre dışı bırak", + "disable_sell": "Satış işlemini devre dışı bırak" } diff --git a/res/values/strings_uk.arb b/res/values/strings_uk.arb index affdda645..a4a65eccc 100644 --- a/res/values/strings_uk.arb +++ b/res/values/strings_uk.arb @@ -707,5 +707,7 @@ "error_text_input_below_minimum_limit" : "Сума менша мінімальної", "error_text_input_above_maximum_limit" : "Сума більше максимальної", "show_market_place":"Відображати маркетплейс", - "prevent_screenshots": "Запобігати знімкам екрана та запису екрана" + "prevent_screenshots": "Запобігати знімкам екрана та запису екрана", + "disable_buy": "Вимкнути дію покупки", + "disable_sell": "Вимкнути дію продажу" } diff --git a/res/values/strings_ur.arb b/res/values/strings_ur.arb index 2e88a0bd6..38deabb0d 100644 --- a/res/values/strings_ur.arb +++ b/res/values/strings_ur.arb @@ -703,5 +703,7 @@ "error_text_input_below_minimum_limit" : "رقم کم از کم سے کم ہے۔", "error_text_input_above_maximum_limit" : "رقم زیادہ سے زیادہ سے زیادہ ہے۔", "show_market_place":"بازار دکھائیں۔", - "prevent_screenshots": "اسکرین شاٹس اور اسکرین ریکارڈنگ کو روکیں۔" + "prevent_screenshots": "اسکرین شاٹس اور اسکرین ریکارڈنگ کو روکیں۔", + "disable_buy": "خرید ایکشن کو غیر فعال کریں۔", + "disable_sell": "فروخت کی کارروائی کو غیر فعال کریں۔" } diff --git a/res/values/strings_zh.arb b/res/values/strings_zh.arb index 10a7d89f2..17f7799bc 100644 --- a/res/values/strings_zh.arb +++ b/res/values/strings_zh.arb @@ -707,5 +707,7 @@ "error_text_input_below_minimum_limit" : "金额小于最小值", "error_text_input_above_maximum_limit" : "金额大于最大值", "show_market_place" :"显示市场", - "prevent_screenshots": "防止截屏和录屏" + "prevent_screenshots": "防止截屏和录屏", + "disable_buy": "禁用购买操作", + "disable_sell": "禁用卖出操作" } diff --git a/scripts/android/app_env.sh b/scripts/android/app_env.sh index 72d3aabfc..98e5762f2 100644 --- a/scripts/android/app_env.sh +++ b/scripts/android/app_env.sh @@ -14,14 +14,14 @@ TYPES=($MONERO_COM $CAKEWALLET $HAVEN) APP_ANDROID_TYPE=$1 MONERO_COM_NAME="Monero.com" -MONERO_COM_VERSION="1.3.5" -MONERO_COM_BUILD_NUMBER=48 +MONERO_COM_VERSION="1.3.6" +MONERO_COM_BUILD_NUMBER=49 MONERO_COM_BUNDLE_ID="com.monero.app" MONERO_COM_PACKAGE="com.monero.app" CAKEWALLET_NAME="Cake Wallet" -CAKEWALLET_VERSION="4.6.4" -CAKEWALLET_BUILD_NUMBER=158 +CAKEWALLET_VERSION="4.6.5" +CAKEWALLET_BUILD_NUMBER=159 CAKEWALLET_BUNDLE_ID="com.cakewallet.cake_wallet" CAKEWALLET_PACKAGE="com.cakewallet.cake_wallet" diff --git a/scripts/ios/app_env.sh b/scripts/ios/app_env.sh index 412ec5aa9..0ee76e82d 100644 --- a/scripts/ios/app_env.sh +++ b/scripts/ios/app_env.sh @@ -13,13 +13,13 @@ TYPES=($MONERO_COM $CAKEWALLET $HAVEN) APP_IOS_TYPE=$1 MONERO_COM_NAME="Monero.com" -MONERO_COM_VERSION="1.3.5" -MONERO_COM_BUILD_NUMBER=46 +MONERO_COM_VERSION="1.3.6" +MONERO_COM_BUILD_NUMBER=47 MONERO_COM_BUNDLE_ID="com.cakewallet.monero" CAKEWALLET_NAME="Cake Wallet" -CAKEWALLET_VERSION="4.6.4" -CAKEWALLET_BUILD_NUMBER=153 +CAKEWALLET_VERSION="4.6.5" +CAKEWALLET_BUILD_NUMBER=154 CAKEWALLET_BUNDLE_ID="com.fotolockr.cakewallet" HAVEN_NAME="Haven" diff --git a/scripts/macos/app_env.sh b/scripts/macos/app_env.sh index 785dc9e24..09a077303 100755 --- a/scripts/macos/app_env.sh +++ b/scripts/macos/app_env.sh @@ -15,8 +15,8 @@ if [ -n "$1" ]; then fi CAKEWALLET_NAME="Cake Wallet" -CAKEWALLET_VERSION="1.0.3" -CAKEWALLET_BUILD_NUMBER=20 +CAKEWALLET_VERSION="1.0.4" +CAKEWALLET_BUILD_NUMBER=22 CAKEWALLET_BUNDLE_ID="com.fotolockr.cakewallet" if ! [[ " ${TYPES[*]} " =~ " ${APP_MACOS_TYPE} " ]]; then