Merge branch 'main' of https://github.com/cake-tech/cake_wallet into CW-328-Restore-wallet-from-QRCode-and-sweep-all-funds-in-a-new-wallet

This commit is contained in:
Blazebrain 2023-05-16 08:30:29 +01:00
commit d3915366a7
64 changed files with 776 additions and 685 deletions

View file

@ -5,9 +5,10 @@
## Links ## Links
* Website: https://cakewallet.com * Website: https://cakewallet.com
* App Store: https://cakewallet.com/ios * App Store (iOS / MacOS): https://cakewallet.com/ios
* Google Play: https://cakewallet.com/gp * Google Play: https://cakewallet.com/gp
* APK: https://github.com/cake-tech/cake_wallet/releases * APK: https://github.com/cake-tech/cake_wallet/releases
* Linux: https://github.com/cake-tech/cake_wallet/releases
## Features ## Features
@ -15,18 +16,22 @@
* Completely noncustodial. *Your keys, your coins.* * Completely noncustodial. *Your keys, your coins.*
* Built-in exchange for dozens of pairs * 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 * 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 * Scan QR codes for easy cryptocurrency transfers
* Create several wallets * Create several wallets
* Select your own custom nodes/servers * Select your own custom nodes/servers
* Address book * Address book
* Backup to external location or iCloud * Backup to external location or iCloud
* Send to OpenAlias, Unstoppable Domains, Yats, and FIO Crypto Handles * Send to OpenAlias, Unstoppable Domains, Yats, and FIO Crypto Handles
* Set custom fee levels * Set desired network fee level
* Store local transaction notes * Store local transaction notes
* Extremely simple user experience * Extremely simple user experience
* Convenient exchange and sending templates for recurring payments * 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 ### Monero Specific Features
@ -34,13 +39,13 @@
* Full support for Monero subaddresses and accounts * Full support for Monero subaddresses and accounts
* Specify restore height for faster syncing * Specify restore height for faster syncing
* Specify multiple recipients for batch sending * Specify multiple recipients for batch sending
* Optionally set Monero nodes as trusted for faster syncing
### Bitcoin Specific Features ### Bitcoin Specific Features
* Bitcoin coin control (specify specific outputs to spend) * Bitcoin coin control (specify specific outputs to spend)
* Automatically generate new addresses * Automatically generate new addresses
* Specify multiple recipients for batch sending * Specify multiple recipients for batch sending
* Buy BTC with over a dozen fiat currencies
* Sell BTC for USD * Sell BTC for USD
### Litecoin Specific Features ### Litecoin Specific Features
@ -48,7 +53,6 @@
* Litecoin coin control (specify specific outputs to spend) * Litecoin coin control (specify specific outputs to spend)
* Automatically generate new addresses * Automatically generate new addresses
* Specify multiple recipients for batch sending * Specify multiple recipients for batch sending
* Buy LTC with over a dozen fiat currencies
### Haven Specific Features ### Haven Specific Features
@ -63,7 +67,7 @@
## Links ## Links
* Website: https://monero.com * 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 * Google Play: https://play.google.com/store/apps/details?id=com.monero.app
* APK: https://github.com/cake-tech/cake_wallet/releases * 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 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 # Build Instructions
More instructions to follow 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. 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. 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

View file

@ -1 +1,3 @@
Fix Restore from QR code Reliability fixes for PIN login, transaction appearance, keyboard inputs, and QR codes
Show amount received by each Monero account in account overview
Other bugfixes

View file

@ -1,4 +1,3 @@
Fix for QR codes Reliability fixes for PIN login, transaction appearance, keyboard inputs, and QR codes
Fix for creating sub-addresses Show amount received by each Monero account in account overview
Fix Add/Edit nodes Other bugfixes
Fix issues with text/amount fields

View file

@ -88,7 +88,7 @@ class ElectrumClient {
unterminatedString = ''; unterminatedString = '';
} }
} on TypeError catch (e) { } on TypeError catch (e) {
if (!e.toString().contains('Map<String, Object>') || !e.toString().contains('Map<String, dynamic>')) { if (!e.toString().contains('Map<String, Object>') && !e.toString().contains('Map<String, dynamic>')) {
return; return;
} }

View file

@ -113,7 +113,7 @@ PODS:
- OrderedSet (~> 5.0) - OrderedSet (~> 5.0)
- flutter_mailer (0.0.1): - flutter_mailer (0.0.1):
- Flutter - Flutter
- flutter_secure_storage (3.3.1): - flutter_secure_storage (6.0.0):
- Flutter - Flutter
- in_app_review (0.2.0): - in_app_review (0.2.0):
- Flutter - Flutter
@ -264,19 +264,19 @@ SPEC CHECKSUMS:
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
flutter_inappwebview: bfd58618f49dc62f2676de690fc6dcda1d6c3721 flutter_inappwebview: bfd58618f49dc62f2676de690fc6dcda1d6c3721
flutter_mailer: 2ef5a67087bc8c6c4cefd04a178bf1ae2c94cd83 flutter_mailer: 2ef5a67087bc8c6c4cefd04a178bf1ae2c94cd83
flutter_secure_storage: 7953c38a04c3fdbb00571bcd87d8e3b5ceb9daec flutter_secure_storage: 23fc622d89d073675f2eaa109381aefbcf5a49be
in_app_review: 318597b3a06c22bb46dc454d56828c85f444f99d in_app_review: 318597b3a06c22bb46dc454d56828c85f444f99d
local_auth_ios: c6cf091ded637a88f24f86a8875d8b0f526e2605 local_auth_ios: c6cf091ded637a88f24f86a8875d8b0f526e2605
MTBBarcodeScanner: f453b33c4b7dfe545d8c6484ed744d55671788cb MTBBarcodeScanner: f453b33c4b7dfe545d8c6484ed744d55671788cb
OrderedSet: aaeb196f7fef5a9edf55d89760da9176ad40b93c OrderedSet: aaeb196f7fef5a9edf55d89760da9176ad40b93c
package_info: 873975fc26034f0b863a300ad47e7f1ac6c7ec62 package_info: 873975fc26034f0b863a300ad47e7f1ac6c7ec62
path_provider_foundation: c68054786f1b4f3343858c1e1d0caaded73f0be9 path_provider_foundation: eaf5b3e458fc0e5fbb9940fb09980e853fe058b8
permission_handler_apple: 44366e37eaf29454a1e7b1b7d736c2cceaeb17ce permission_handler_apple: 44366e37eaf29454a1e7b1b7d736c2cceaeb17ce
platform_device_id: 81b3e2993881f87d0c82ef151dc274df4869aef5 platform_device_id: 81b3e2993881f87d0c82ef151dc274df4869aef5
Reachability: 33e18b67625424e47b6cde6d202dce689ad7af96 Reachability: 33e18b67625424e47b6cde6d202dce689ad7af96
SDWebImage: fd7e1a22f00303e058058278639bf6196ee431fe SDWebImage: fd7e1a22f00303e058058278639bf6196ee431fe
share_plus: 056a1e8ac890df3e33cb503afffaf1e9b4fbae68 share_plus: 056a1e8ac890df3e33cb503afffaf1e9b4fbae68
shared_preferences_foundation: 986fc17f3d3251412d18b0265f9c64113a8c2472 shared_preferences_foundation: e2dae3258e06f44cc55f49d42024fd8dd03c590c
SwiftProtobuf: afced68785854575756db965e9da52bbf3dc45e7 SwiftProtobuf: afced68785854575756db965e9da52bbf3dc45e7
SwiftyGif: 93a1cc87bf3a51916001cf8f3d63835fb64c819f SwiftyGif: 93a1cc87bf3a51916001cf8f3d63835fb64c819f
uni_links: d97da20c7701486ba192624d99bffaaffcfc298a uni_links: d97da20c7701486ba192624d99bffaaffcfc298a

View file

@ -52,16 +52,6 @@
<string>bitcoin-wallet</string> <string>bitcoin-wallet</string>
</array> </array>
</dict> </dict>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>bitcoin_wallet</string>
<key>CFBundleURLSchemes</key>
<array>
<string>bitcoin_wallet</string>
</array>
</dict>
<dict> <dict>
<key>CFBundleTypeRole</key> <key>CFBundleTypeRole</key>
<string>Editor</string> <string>Editor</string>
@ -82,16 +72,6 @@
<string>monero-wallet</string> <string>monero-wallet</string>
</array> </array>
</dict> </dict>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>monero_wallet</string>
<key>CFBundleURLSchemes</key>
<array>
<string>monero_wallet</string>
</array>
</dict>
<dict> <dict>
<key>CFBundleTypeRole</key> <key>CFBundleTypeRole</key>
<string>Editor</string> <string>Editor</string>
@ -112,16 +92,6 @@
<string>litecoin-wallet</string> <string>litecoin-wallet</string>
</array> </array>
</dict> </dict>
<dict>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>CFBundleURLName</key>
<string>litecoin_wallet</string>
<key>CFBundleURLSchemes</key>
<array>
<string>litecoin_wallet</string>
</array>
</dict>
</array> </array>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string> <string>$(CURRENT_PROJECT_VERSION)</string>

View file

@ -1,6 +1,7 @@
import 'package:cake_wallet/.secrets.g.dart' as secrets; import 'package:cake_wallet/.secrets.g.dart' as secrets;
import 'package:cake_wallet/store/settings_store.dart'; import 'package:cake_wallet/store/settings_store.dart';
import 'package:cake_wallet/themes/theme_base.dart'; import 'package:cake_wallet/themes/theme_base.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/wallet_base.dart'; import 'package:cw_core/wallet_base.dart';
class OnRamperBuyProvider { class OnRamperBuyProvider {
@ -13,7 +14,16 @@ class OnRamperBuyProvider {
static const _baseUrl = 'buy.onramper.com'; 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() { Uri requestUrl() {
String primaryColor, String primaryColor,
@ -53,7 +63,7 @@ class OnRamperBuyProvider {
return Uri.https(_baseUrl, '', <String, dynamic>{ return Uri.https(_baseUrl, '', <String, dynamic>{
'apiKey': _apiKey, 'apiKey': _apiKey,
'defaultCrypto': _wallet.currency.title, 'defaultCrypto': _normalizeCryptoCurrency,
'defaultFiat': _settingsStore.fiatCurrency.title, 'defaultFiat': _settingsStore.fiatCurrency.title,
'wallets': '${_wallet.currency.title}:${_wallet.walletAddresses.address}', 'wallets': '${_wallet.currency.title}:${_wallet.walletAddresses.address}',
'supportSell': "false", 'supportSell': "false",

View file

@ -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/generated/i18n.dart';
import 'package:cake_wallet/core/validator.dart'; import 'package:cake_wallet/core/validator.dart';
import 'package:cw_core/crypto_currency.dart'; import 'package:cw_core/crypto_currency.dart';
@ -7,6 +7,9 @@ class AddressValidator extends TextValidator {
AddressValidator({required CryptoCurrency type}) AddressValidator({required CryptoCurrency type})
: super( : super(
errorMessage: S.current.error_text_address, errorMessage: S.current.error_text_address,
useAdditionalValidation: type == CryptoCurrency.btc
? bitcoin.Address.validateAddress
: null,
pattern: getPattern(type), pattern: getPattern(type),
length: getLength(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}\$' 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}\$'; '|^[0-9a-zA-Z]{105}\$|^addr1[0-9a-zA-Z]{98}\$';
case CryptoCurrency.btc: case CryptoCurrency.btc:
return '^1[0-9a-zA-Z]{32}\$|^1[0-9a-zA-Z]{33}\$|^3[0-9a-zA-Z]{32}\$' return '^3[0-9a-zA-Z]{32}\$|^3[0-9a-zA-Z]{33}\$|^bc1[0-9a-zA-Z]{59}\$';
'|^3[0-9a-zA-Z]{33}\$|^bc1[0-9a-zA-Z]{39}\$|^bc1[0-9a-zA-Z]{59}\$';
case CryptoCurrency.nano: case CryptoCurrency.nano:
return '[0-9a-zA-Z_]'; return '[0-9a-zA-Z_]';
case CryptoCurrency.usdc: case CryptoCurrency.usdc:
@ -63,7 +65,7 @@ class AddressValidator extends TextValidator {
case CryptoCurrency.bch: case CryptoCurrency.bch:
case CryptoCurrency.bnb: case CryptoCurrency.bnb:
return '[0-9a-zA-Z]'; return '[0-9a-zA-Z]';
case CryptoCurrency.hbar: case CryptoCurrency.hbar:
return '[0-9a-zA-Z.]'; return '[0-9a-zA-Z.]';
case CryptoCurrency.zaddr: case CryptoCurrency.zaddr:
return '^zs[0-9a-zA-Z]{75}'; return '^zs[0-9a-zA-Z]{75}';
@ -165,9 +167,9 @@ class AddressValidator extends TextValidator {
return [34]; return [34];
case CryptoCurrency.hbar: case CryptoCurrency.hbar:
return [4, 5, 6, 7, 8, 9, 10, 11]; return [4, 5, 6, 7, 8, 9, 10, 11];
case CryptoCurrency.xvg: case CryptoCurrency.xvg:
return [34]; return [34];
case CryptoCurrency.zen: case CryptoCurrency.zen:
return [35]; return [35];
case CryptoCurrency.zaddr: case CryptoCurrency.zaddr:
return null; return null;

View file

@ -210,6 +210,8 @@ class BackupService {
final currentFiatCurrency = data[PreferencesKey.currentFiatCurrencyKey] as String?; final currentFiatCurrency = data[PreferencesKey.currentFiatCurrencyKey] as String?;
final shouldSaveRecipientAddress = data[PreferencesKey.shouldSaveRecipientAddressKey] as bool?; final shouldSaveRecipientAddress = data[PreferencesKey.shouldSaveRecipientAddressKey] as bool?;
final isAppSecure = data[PreferencesKey.isAppSecureKey] 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 currentTransactionPriorityKeyLegacy = data[PreferencesKey.currentTransactionPriorityKeyLegacy] as int?;
final allowBiometricalAuthentication = data[PreferencesKey.allowBiometricalAuthenticationKey] as bool?; final allowBiometricalAuthentication = data[PreferencesKey.allowBiometricalAuthenticationKey] as bool?;
final currentBitcoinElectrumSererId = data[PreferencesKey.currentBitcoinElectrumSererIdKey] as int?; final currentBitcoinElectrumSererId = data[PreferencesKey.currentBitcoinElectrumSererIdKey] as int?;
@ -251,6 +253,16 @@ class BackupService {
PreferencesKey.isAppSecureKey, PreferencesKey.isAppSecureKey,
isAppSecure); isAppSecure);
if (disableBuy != null)
await _sharedPreferences.setBool(
PreferencesKey.disableBuyKey,
disableBuy);
if (disableSell != null)
await _sharedPreferences.setBool(
PreferencesKey.disableSellKey,
disableSell);
if (currentTransactionPriorityKeyLegacy != null) if (currentTransactionPriorityKeyLegacy != null)
await _sharedPreferences.setInt( await _sharedPreferences.setInt(
PreferencesKey.currentTransactionPriorityKeyLegacy, PreferencesKey.currentTransactionPriorityKeyLegacy,
@ -421,6 +433,10 @@ class BackupService {
_sharedPreferences.getString(PreferencesKey.currentFiatCurrencyKey), _sharedPreferences.getString(PreferencesKey.currentFiatCurrencyKey),
PreferencesKey.shouldSaveRecipientAddressKey: _sharedPreferences PreferencesKey.shouldSaveRecipientAddressKey: _sharedPreferences
.getBool(PreferencesKey.shouldSaveRecipientAddressKey), .getBool(PreferencesKey.shouldSaveRecipientAddressKey),
PreferencesKey.disableBuyKey: _sharedPreferences
.getBool(PreferencesKey.disableBuyKey),
PreferencesKey.disableSellKey: _sharedPreferences
.getBool(PreferencesKey.disableSellKey),
PreferencesKey.isDarkThemeLegacy: PreferencesKey.isDarkThemeLegacy:
_sharedPreferences.getBool(PreferencesKey.isDarkThemeLegacy), _sharedPreferences.getBool(PreferencesKey.isDarkThemeLegacy),
PreferencesKey.currentPinLength: PreferencesKey.currentPinLength:

View file

@ -1,24 +1,26 @@
import 'package:flutter/foundation.dart';
abstract class Validator<T> { abstract class Validator<T> {
Validator({required this.errorMessage}); Validator({required this.errorMessage, this.useAdditionalValidation});
final String errorMessage; final String errorMessage;
final bool Function(T)? useAdditionalValidation;
bool isValid(T? value); bool isValid(T? value);
String? call(T? value) => !isValid(value) ? errorMessage : null; String? call(T? value) => !isValid(value) ? errorMessage : null;
} }
class TextValidator extends Validator<String> { class TextValidator extends Validator<String> {
TextValidator( TextValidator({
{this.minLength, bool Function(String)? useAdditionalValidation,
this.maxLength, this.minLength,
this.pattern, this.maxLength,
String errorMessage = '', this.pattern,
this.length, String errorMessage = '',
this.isAutovalidate = false}) this.length,
: super(errorMessage: errorMessage); this.isAutovalidate = false,
}) : super(
errorMessage: errorMessage,
useAdditionalValidation: useAdditionalValidation);
final int? minLength; final int? minLength;
final int? maxLength; final int? maxLength;
@ -32,11 +34,26 @@ class TextValidator extends Validator<String> {
return isAutovalidate ? true : false; return isAutovalidate ? true : false;
} }
return value.length > (minLength ?? 0) && final greaterThanMinLength = value.length > (minLength ?? 0);
(length?.contains(value.length) ?? true) && if (!greaterThanMinLength) return false;
((maxLength ?? 0) > 0 ? (value.length <= maxLength!) : true) &&
(pattern != null ? match(value) : true); 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;
} }

View file

@ -47,23 +47,23 @@ class MainActions {
switch (walletType) { switch (walletType) {
case WalletType.bitcoin: case WalletType.bitcoin:
case WalletType.litecoin: case WalletType.litecoin:
if (DeviceInfo.instance.isMobile) { if (viewModel.isEnabledBuyAction) {
Navigator.of(context).pushNamed(Routes.onramperPage); if (DeviceInfo.instance.isMobile) {
} else { Navigator.of(context).pushNamed(Routes.onramperPage);
final uri = getIt } else {
.get<OnRamperBuyProvider>() final uri = getIt.get<OnRamperBuyProvider>().requestUrl();
.requestUrl(); await launchUrl(uri);
await launchUrl(uri); }
} }
break; break;
case WalletType.monero: case WalletType.monero:
if (DeviceInfo.instance.isMobile) { if (viewModel.isEnabledBuyAction) {
Navigator.of(context).pushNamed(Routes.payfuraPage); if (DeviceInfo.instance.isMobile) {
} else { Navigator.of(context).pushNamed(Routes.payfuraPage);
final uri = getIt } else {
.get<PayfuraBuyProvider>() final uri = getIt.get<PayfuraBuyProvider>().requestUrl();
.requestUrl(); await launchUrl(uri);
await launchUrl(uri); }
} }
break; break;
default: default:
@ -118,12 +118,14 @@ class MainActions {
switch (walletType) { switch (walletType) {
case WalletType.bitcoin: case WalletType.bitcoin:
final moonPaySellProvider = MoonPaySellProvider(); if (viewModel.isEnabledSellAction) {
final uri = await moonPaySellProvider.requestUrl( final moonPaySellProvider = MoonPaySellProvider();
currency: viewModel.wallet.currency, final uri = await moonPaySellProvider.requestUrl(
refundWalletAddress: viewModel.wallet.walletAddresses.address, currency: viewModel.wallet.currency,
); refundWalletAddress: viewModel.wallet.walletAddresses.address,
await launchUrl(uri); );
await launchUrl(uri);
}
break; break;
default: default:
await showPopUp<void>( await showPopUp<void>(

View file

@ -10,6 +10,8 @@ class PreferencesKey {
static const currentBalanceDisplayModeKey = 'current_balance_display_mode'; static const currentBalanceDisplayModeKey = 'current_balance_display_mode';
static const shouldSaveRecipientAddressKey = 'save_recipient_address'; static const shouldSaveRecipientAddressKey = 'save_recipient_address';
static const isAppSecureKey = 'is_app_secure'; 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 currentFiatApiModeKey = 'current_fiat_api_mode';
static const allowBiometricalAuthenticationKey = static const allowBiometricalAuthenticationKey =
'allow_biometrical_authentication'; 'allow_biometrical_authentication';

View file

@ -169,6 +169,7 @@ Route<dynamic> createRoute(RouteSettings settings) {
fullscreenDialog: true); fullscreenDialog: true);
} else if (isSingleCoin) { } else if (isSingleCoin) {
return MaterialPageRoute<void>( return MaterialPageRoute<void>(
fullscreenDialog: true,
builder: (_) => getIt.get<WalletRestorePage>( builder: (_) => getIt.get<WalletRestorePage>(
param1: availableWalletTypes.first param1: availableWalletTypes.first
)); ));
@ -188,6 +189,7 @@ Route<dynamic> createRoute(RouteSettings settings) {
case Routes.restoreWallet: case Routes.restoreWallet:
return MaterialPageRoute<void>( return MaterialPageRoute<void>(
fullscreenDialog: true,
builder: (_) => getIt.get<WalletRestorePage>( builder: (_) => getIt.get<WalletRestorePage>(
param1: settings.arguments as WalletType)); param1: settings.arguments as WalletType));
@ -509,7 +511,9 @@ Route<dynamic> createRoute(RouteSettings settings) {
case Routes.anonPayInvoicePage: case Routes.anonPayInvoicePage:
final args = settings.arguments as List; final args = settings.arguments as List;
return CupertinoPageRoute<void>(builder: (_) => getIt.get<AnonPayInvoicePage>(param1: args)); return CupertinoPageRoute<void>(
fullscreenDialog: true,
builder: (_) => getIt.get<AnonPayInvoicePage>(param1: args));
case Routes.anonPayReceivePage: case Routes.anonPayReceivePage:
final anonInvoiceViewData = settings.arguments as AnonpayInfoBase; final anonInvoiceViewData = settings.arguments as AnonpayInfoBase;

View file

@ -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/palette.dart';
import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cw_core/currency.dart'; import 'package:cw_core/currency.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:mobx/mobx.dart'; import 'package:mobx/mobx.dart';
import 'package:cake_wallet/generated/i18n.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/contact_name_validator.dart';
import 'package:cake_wallet/core/execution_state.dart'; import 'package:cake_wallet/core/execution_state.dart';
import 'package:cake_wallet/view_model/contact_list/contact_view_model.dart'; import 'package:cake_wallet/view_model/contact_list/contact_view_model.dart';
@ -33,8 +31,8 @@ class ContactPage extends BasePage {
_addressController _addressController
.addListener(() => contactViewModel.address = _addressController.text); .addListener(() => contactViewModel.address = _addressController.text);
autorun((_) => autorun((_) => _currencyTypeController.text =
_currencyTypeController.text = contactViewModel.currency?.toString()??''); contactViewModel.currency?.toString() ?? '');
} }
@override @override
@ -61,96 +59,105 @@ class ContactPage extends BasePage {
} }
}); });
return ScrollableWithBottomSection( return Observer(
contentPadding: EdgeInsets.all(24), builder: (_) => ScrollableWithBottomSection(
content: Form( contentPadding: EdgeInsets.all(24),
key: _formKey, content: Form(
child: Column( key: _formKey,
mainAxisSize: MainAxisSize.min, child: Column(
children: <Widget>[ mainAxisSize: MainAxisSize.min,
BaseTextFormField( children: <Widget>[
controller: _nameController, BaseTextFormField(
hintText: S.of(context).contact_name, controller: _nameController,
validator: ContactNameValidator()), hintText: S.of(context).contact_name,
Padding( validator: ContactNameValidator()),
padding: EdgeInsets.only(top: 20), Padding(
child: Container( padding: EdgeInsets.only(top: 20),
child: InkWell( child: Container(
onTap: () => _presentCurrencyPicker(context), child: InkWell(
child: IgnorePointer( onTap: () => _presentCurrencyPicker(context),
child: BaseTextFormField( child: IgnorePointer(
controller: _currencyTypeController, child: BaseTextFormField(
hintText: S.of(context).settings_currency, controller: _currencyTypeController,
suffixIcon: Row( hintText: S.of(context).settings_currency,
mainAxisSize: MainAxisSize.min, suffixIcon: Row(
mainAxisAlignment: MainAxisAlignment.end, mainAxisSize: MainAxisSize.min,
children: <Widget>[downArrow], mainAxisAlignment: MainAxisAlignment.end,
), children: <Widget>[downArrow],
)), ),
)),
),
), ),
), ),
), if (contactViewModel.currency != null)
Padding( Padding(
padding: EdgeInsets.only(top: 20), padding: EdgeInsets.only(top: 20),
child: Observer( child: AddressTextField(
builder: (_) => AddressTextField(
controller: _addressController, controller: _addressController,
options: [ options: [
AddressTextFieldOption.paste, AddressTextFieldOption.paste,
AddressTextFieldOption.qrCode, AddressTextFieldOption.qrCode,
], ],
buttonColor: Theme.of(context).accentTextTheme!.headline3!.color!, buttonColor:
Theme.of(context).accentTextTheme!.headline3!.color!,
iconColor: PaletteDark.gray, iconColor: PaletteDark.gray,
borderColor: Theme.of(context).primaryTextTheme!.headline6!.backgroundColor!, borderColor: Theme.of(context)
validator: TextValidator() .primaryTextTheme!
// AddressValidator( .headline6!
// type: contactViewModel.currency), .backgroundColor!,
)), validator:
) AddressValidator(type: contactViewModel.currency!),
], ),
), )
), ],
bottomSectionPadding:
EdgeInsets.only(left: 24, right: 24, bottom: 24),
bottomSection: Row(
children: <Widget>[
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( bottomSectionPadding:
child: Observer( EdgeInsets.only(left: 24, right: 24, bottom: 24),
builder: (_) => PrimaryButton( bottomSection: Row(
onPressed: () async { children: <Widget>[
if (_formKey.currentState != null && !_formKey.currentState!.validate()) { Expanded(
return; 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(); await contactViewModel.save();
}, },
text: S.of(context).save, text: S.of(context).save,
color: Theme.of(context).accentTextTheme!.bodyText1!.color!, color: Theme.of(context)
textColor: Colors.white, .accentTextTheme!
isDisabled: !contactViewModel.isReady))) .bodyText1!
], .color!,
)); textColor: Colors.white,
isDisabled: !contactViewModel.isReady)))
],
)),
);
} }
void _presentCurrencyPicker(BuildContext context) { void _presentCurrencyPicker(BuildContext context) {
showPopUp<void>( showPopUp<void>(
builder: (_) => CurrencyPicker( builder: (_) => CurrencyPicker(
selectedAtIndex: selectedAtIndex: contactViewModel.currency != null
contactViewModel.currency != null ? contactViewModel.currencies
? contactViewModel.currencies.indexOf(contactViewModel.currency!) .indexOf(contactViewModel.currency!)
: 0, : -1,
items: contactViewModel.currencies, items: contactViewModel.currencies,
title: S.of(context).please_select, title: S.of(context).please_select,
hintText: S.of(context).search_currency, hintText: S.of(context).search_currency,

View file

@ -96,7 +96,10 @@ class AddressPage extends BasePage {
@override @override
Widget middle(BuildContext context) => Widget middle(BuildContext context) =>
PresentReceiveOptionPicker(receiveOptionViewModel: receiveOptionViewModel); PresentReceiveOptionPicker(
receiveOptionViewModel: receiveOptionViewModel,
hasWhiteBackground: currentTheme.type == ThemeType.light,
);
@override @override
Widget Function(BuildContext, Widget) get rootWrapper => Widget Function(BuildContext, Widget) get rootWrapper =>

View file

@ -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/screens/dashboard/widgets/filter_tile.dart';
import 'package:cake_wallet/src/widgets/section_divider.dart'; import 'package:cake_wallet/src/widgets/section_divider.dart';
import 'package:cake_wallet/src/widgets/standard_checkbox.dart'; import 'package:cake_wallet/src/widgets/standard_checkbox.dart';
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart'; import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:cake_wallet/src/widgets/alert_background.dart'; import 'package:cake_wallet/src/widgets/picker_wrapper_widget.dart';
import 'package:cake_wallet/src/widgets/alert_close_button.dart';
import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/generated/i18n.dart';
import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:flutter_mobx/flutter_mobx.dart';
//import 'package:date_range_picker/date_range_picker.dart' as date_rage_picker; //import 'package:date_range_picker/date_range_picker.dart' as date_rage_picker;
@ -20,89 +16,93 @@ class FilterWidget extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
const sectionDivider = const SectionDivider(); const sectionDivider = const SectionDivider();
return AlertBackground( return PickerWrapperWidget(
child: Stack( children: [
alignment: Alignment.center, Padding(
children: <Widget>[ padding: EdgeInsets.only(left: 24, right: 24, top: 24),
Column( child: ClipRRect(
mainAxisSize: MainAxisSize.min, borderRadius: BorderRadius.all(Radius.circular(24)),
children: <Widget>[ child: Container(
Padding( color: Theme.of(context).textTheme!.bodyText1!.decorationColor!,
padding: EdgeInsets.only(left: 24, right: 24, top: 24), child: Column(
child: ClipRRect( crossAxisAlignment: CrossAxisAlignment.start,
borderRadius: BorderRadius.all(Radius.circular(24)), children: [
child: Container( Padding(
color: Theme.of(context).textTheme!.bodyText1!.decorationColor!, padding: EdgeInsets.all(24.0),
child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ child: Text(
Padding( S.of(context).filter_by,
padding: EdgeInsets.all(24.0), style: TextStyle(
child: Text( color: Theme.of(context)
S.of(context).filter_by, .primaryTextTheme
style: TextStyle( .overline!
color: Theme.of(context).primaryTextTheme.overline!.color!, .color!,
fontSize: 16, fontSize: 16,
fontFamily: 'Lato', fontFamily: 'Lato',
decoration: TextDecoration.none, decoration: TextDecoration.none,
),
), ),
), ),
sectionDivider, ),
ListView.separated( sectionDivider,
padding: EdgeInsets.zero, ListView.separated(
shrinkWrap: true, padding: EdgeInsets.zero,
physics: const NeverScrollableScrollPhysics(), shrinkWrap: true,
itemCount: dashboardViewModel.filterItems.length, physics: const NeverScrollableScrollPhysics(),
separatorBuilder: (context, _) => sectionDivider, itemCount: dashboardViewModel.filterItems.length,
itemBuilder: (_, index1) { separatorBuilder: (context, _) => sectionDivider,
final title = dashboardViewModel.filterItems.keys.elementAt(index1); itemBuilder: (_, index1) {
final section = dashboardViewModel.filterItems.values.elementAt(index1); final title = dashboardViewModel.filterItems.keys
return Column( .elementAt(index1);
crossAxisAlignment: CrossAxisAlignment.start, final section = dashboardViewModel.filterItems.values
children: <Widget>[ .elementAt(index1);
Padding( return Column(
padding: EdgeInsets.only(top: 20, left: 24, right: 24), crossAxisAlignment: CrossAxisAlignment.start,
child: Text( children: <Widget>[
title, Padding(
style: TextStyle( padding:
color: Theme.of(context).primaryTextTheme!.headline6!.color!, EdgeInsets.only(top: 20, left: 24, right: 24),
fontSize: 16, child: Text(
fontFamily: 'Lato', title,
fontWeight: FontWeight.bold, style: TextStyle(
decoration: TextDecoration.none), 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), ListView.builder(
shrinkWrap: true, padding: EdgeInsets.symmetric(vertical: 8.0),
physics: const NeverScrollableScrollPhysics(), shrinkWrap: true,
itemCount: section.length, physics: const NeverScrollableScrollPhysics(),
itemBuilder: (_, index2) { itemCount: section.length,
final item = section[index2]; itemBuilder: (_, index2) {
final content = Observer( final item = section[index2];
builder: (_) => StandardCheckbox( final content = Observer(
value: item.value(), builder: (_) => StandardCheckbox(
caption: item.caption, value: item.value(),
gradientBackground: true, caption: item.caption,
borderColor: Theme.of(context).dividerColor, gradientBackground: true,
iconColor: Colors.white, borderColor:
onChanged: (value) => item.onChanged(), Theme.of(context).dividerColor,
)); iconColor: Colors.white,
return FilterTile(child: content); onChanged: (value) =>
}, item.onChanged(),
) ));
], return FilterTile(child: content);
); },
}, )
), ],
]), );
), },
), ),
), ]),
], ),
), ),
AlertCloseButton() )
], ],
),
); );
} }
} }

View file

@ -9,14 +9,22 @@ import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/generated/i18n.dart';
class PresentReceiveOptionPicker extends StatelessWidget { class PresentReceiveOptionPicker extends StatelessWidget {
PresentReceiveOptionPicker({required this.receiveOptionViewModel}); PresentReceiveOptionPicker(
{required this.receiveOptionViewModel, this.hasWhiteBackground = false});
final ReceiveOptionViewModel receiveOptionViewModel; final ReceiveOptionViewModel receiveOptionViewModel;
final bool hasWhiteBackground;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final arrowBottom = final textIconTheme = hasWhiteBackground
Image.asset('assets/images/arrow_bottom_purple_icon.png', color: Colors.white, height: 6); ? 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( return TextButton(
onPressed: () => _showPicker(context), onPressed: () => _showPicker(context),
@ -40,14 +48,14 @@ class PresentReceiveOptionPicker extends StatelessWidget {
fontSize: 18.0, fontSize: 18.0,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
fontFamily: 'Lato', fontFamily: 'Lato',
color: Theme.of(context).accentTextTheme.headline2!.backgroundColor!), color: textIconTheme),
), ),
Observer( Observer(
builder: (_) => Text(receiveOptionViewModel.selectedReceiveOption.toString(), builder: (_) => Text(receiveOptionViewModel.selectedReceiveOption.toString(),
style: TextStyle( style: TextStyle(
fontSize: 10.0, fontSize: 10.0,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
color: Theme.of(context).textTheme.headline5!.color!))) color: textIconTheme)))
], ],
), ),
SizedBox(width: 5), SizedBox(width: 5),

View file

@ -32,7 +32,7 @@ class AnonpayCurrencyInputField extends StatelessWidget {
decoration: BoxDecoration( decoration: BoxDecoration(
border: Border( border: Border(
bottom: BorderSide( bottom: BorderSide(
color: Theme.of(context).accentTextTheme.headline6!.backgroundColor!, color: Theme.of(context).primaryTextTheme.bodyText1!.color!,
width: 1)), width: 1)),
), ),
child: Padding( child: Padding(

View file

@ -69,7 +69,7 @@ class AnonInvoiceForm extends StatelessWidget {
BaseTextFormField( BaseTextFormField(
controller: nameController, controller: nameController,
focusNode: _nameFocusNode, focusNode: _nameFocusNode,
borderColor: Theme.of(context).accentTextTheme.headline6!.backgroundColor, borderColor: Theme.of(context).primaryTextTheme.bodyText1!.color!,
suffixIcon: SizedBox(width: 36), suffixIcon: SizedBox(width: 36),
hintText: S.of(context).optional_name, hintText: S.of(context).optional_name,
textInputAction: TextInputAction.next, textInputAction: TextInputAction.next,
@ -88,7 +88,7 @@ class AnonInvoiceForm extends StatelessWidget {
controller: descriptionController, controller: descriptionController,
focusNode: _descriptionFocusNode, focusNode: _descriptionFocusNode,
textInputAction: TextInputAction.next, textInputAction: TextInputAction.next,
borderColor: Theme.of(context).accentTextTheme.headline6!.backgroundColor, borderColor: Theme.of(context).primaryTextTheme.bodyText1!.color!,
suffixIcon: SizedBox(width: 36), suffixIcon: SizedBox(width: 36),
hintText: S.of(context).optional_description, hintText: S.of(context).optional_description,
placeholderTextStyle: TextStyle( placeholderTextStyle: TextStyle(
@ -104,7 +104,7 @@ class AnonInvoiceForm extends StatelessWidget {
controller: emailController, controller: emailController,
textInputAction: TextInputAction.next, textInputAction: TextInputAction.next,
focusNode: _emailFocusNode, focusNode: _emailFocusNode,
borderColor: Theme.of(context).accentTextTheme.headline6!.backgroundColor, borderColor: Theme.of(context).primaryTextTheme.bodyText1!.color!,
suffixIcon: SizedBox(width: 36), suffixIcon: SizedBox(width: 36),
keyboardType: TextInputType.emailAddress, keyboardType: TextInputType.emailAddress,
hintText: S.of(context).optional_email_hint, hintText: S.of(context).optional_email_hint,

View file

@ -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:cake_wallet/src/widgets/base_text_form_field.dart';
import 'package:cw_core/currency.dart'; import 'package:cw_core/currency.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -10,18 +9,20 @@ class CurrencyInputField extends StatelessWidget {
required this.onTapPicker, required this.onTapPicker,
required this.selectedCurrency, required this.selectedCurrency,
this.focusNode, this.focusNode,
required this.controller, required this.controller, required this.isLight,
}); });
final Function() onTapPicker; final Function() onTapPicker;
final Currency selectedCurrency; final Currency selectedCurrency;
final FocusNode? focusNode; final FocusNode? focusNode;
final TextEditingController controller; final TextEditingController controller;
final bool isLight;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final arrowBottomPurple = Image.asset( final arrowBottomPurple = Image.asset(
'assets/images/arrow_bottom_purple_icon.png', 'assets/images/arrow_bottom_purple_icon.png',
color: Colors.white, color: Theme.of(context).accentTextTheme.headline2!.backgroundColor!,
height: 8, height: 8,
); );
final _width = MediaQuery.of(context).size.width; final _width = MediaQuery.of(context).size.width;
@ -38,14 +39,14 @@ class CurrencyInputField extends StatelessWidget {
keyboardType: TextInputType.numberWithOptions(signed: false, decimal: true), keyboardType: TextInputType.numberWithOptions(signed: false, decimal: true),
inputFormatters: [FilteringTextInputFormatter.allow(RegExp(r'^\d+(\.|\,)?\d{0,8}'))], inputFormatters: [FilteringTextInputFormatter.allow(RegExp(r'^\d+(\.|\,)?\d{0,8}'))],
hintText: '0.000', hintText: '0.000',
placeholderTextStyle: TextStyle( placeholderTextStyle: isLight ? null : TextStyle(
color: Theme.of(context).primaryTextTheme.headline5!.color!, color: Theme.of(context).primaryTextTheme.headline5!.color!,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
), ),
borderColor: Theme.of(context).accentTextTheme.headline6!.backgroundColor!, borderColor: Theme.of(context).accentTextTheme.headline6!.backgroundColor!,
textColor: Colors.white, textColor: Theme.of(context).accentTextTheme.headline2!.backgroundColor!,
textStyle: TextStyle( textStyle: TextStyle(
color: Colors.white, color: Theme.of(context).accentTextTheme.headline2!.backgroundColor!,
), ),
prefixIcon: Padding( prefixIcon: Padding(
padding: EdgeInsets.only( padding: EdgeInsets.only(
@ -68,7 +69,7 @@ class CurrencyInputField extends StatelessWidget {
style: TextStyle( style: TextStyle(
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
fontSize: 16, fontSize: 16,
color: Colors.white, color: Theme.of(context).accentTextTheme.headline2!.backgroundColor!,
), ),
), ),
if (selectedCurrency.tag != null) if (selectedCurrency.tag != null)
@ -103,7 +104,8 @@ class CurrencyInputField extends StatelessWidget {
style: TextStyle( style: TextStyle(
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
fontSize: 20, fontSize: 20,
color: Colors.white, color:
Theme.of(context).accentTextTheme.headline2!.backgroundColor!,
), ),
), ),
), ),

View file

@ -23,7 +23,7 @@ class QrImage extends StatelessWidget {
size: size, size: size,
foregroundColor: Colors.black, foregroundColor: Colors.black,
backgroundColor: Colors.white, backgroundColor: Colors.white,
padding: EdgeInsets.zero, padding: const EdgeInsets.all(8.0),
); );
} }
} }

View file

@ -119,6 +119,7 @@ class QRWidget extends StatelessWidget {
controller: amountController, controller: amountController,
onTapPicker: () => _presentPicker(context), onTapPicker: () => _presentPicker(context),
selectedCurrency: addressListViewModel.selectedCurrency, selectedCurrency: addressListViewModel.selectedCurrency,
isLight: isLight,
), ),
), ),
), ),

View file

@ -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<RestoreFromKeysFrom> {
final _formKey = GlobalKey<FormState>();
final _blockchainHeightKey = GlobalKey<BlockchainHeightState>();
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: <Widget>[
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);
}),
]),
),
);
}
}

View file

@ -548,6 +548,10 @@ class SendCardState extends State<SendCard>
} }
void _setEffects(BuildContext context) { void _setEffects(BuildContext context) {
if (_effectsInstalled) {
return;
}
if (output.address.isNotEmpty) { if (output.address.isNotEmpty) {
addressController.text = output.address; addressController.text = output.address;
} }
@ -558,10 +562,6 @@ class SendCardState extends State<SendCard>
noteController.text = output.note; noteController.text = output.note;
extractedAddressController.text = output.extractedAddress; extractedAddressController.text = output.extractedAddress;
if (_effectsInstalled) {
return;
}
cryptoAmountController.addListener(() { cryptoAmountController.addListener(() {
final amount = cryptoAmountController.text; final amount = cryptoAmountController.text;

View file

@ -50,11 +50,23 @@ class PrivacyPage extends BasePage {
_privacySettingsViewModel.setShouldSaveRecipientAddress(value); _privacySettingsViewModel.setShouldSaveRecipientAddress(value);
}), }),
if (Platform.isAndroid) if (Platform.isAndroid)
SettingsSwitcherCell(
title: S.current.prevent_screenshots,
value: _privacySettingsViewModel.isAppSecure,
onValueChange: (BuildContext _, bool value) {
_privacySettingsViewModel.setIsAppSecure(value);
}),
SettingsSwitcherCell( SettingsSwitcherCell(
title: S.current.prevent_screenshots, title: S.current.disable_buy,
value: _privacySettingsViewModel.isAppSecure, value: _privacySettingsViewModel.disableBuy,
onValueChange: (BuildContext _, bool value) { 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);
}), }),
], ],
); );

View file

@ -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_picker_cell.dart';
import 'package:cake_wallet/src/screens/settings/widgets/settings_switcher_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/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:cake_wallet/view_model/settings/security_settings_view_model.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:flutter_mobx/flutter_mobx.dart';
@ -48,29 +49,30 @@ class SecurityBackupPage extends BasePage {
), ),
), ),
StandardListSeparator(padding: EdgeInsets.symmetric(horizontal: 24)), StandardListSeparator(padding: EdgeInsets.symmetric(horizontal: 24)),
Observer(builder: (_) { if (DeviceInfo.instance.isMobile)
return SettingsSwitcherCell( Observer(builder: (_) {
title: S.current.settings_allow_biometrical_authentication, return SettingsSwitcherCell(
value: _securitySettingsViewModel.allowBiometricalAuthentication, title: S.current.settings_allow_biometrical_authentication,
onValueChange: (BuildContext context, bool value) { value: _securitySettingsViewModel.allowBiometricalAuthentication,
if (value) { onValueChange: (BuildContext context, bool value) {
_authService.authenticateAction(context, if (value) {
onAuthSuccess: (isAuthenticatedSuccessfully) async { _authService.authenticateAction(context,
if (isAuthenticatedSuccessfully) { onAuthSuccess: (isAuthenticatedSuccessfully) async {
if (await _securitySettingsViewModel.biometricAuthenticated()) { if (isAuthenticatedSuccessfully) {
if (await _securitySettingsViewModel.biometricAuthenticated()) {
_securitySettingsViewModel
.setAllowBiometricalAuthentication(isAuthenticatedSuccessfully);
}
} else {
_securitySettingsViewModel _securitySettingsViewModel
.setAllowBiometricalAuthentication(isAuthenticatedSuccessfully); .setAllowBiometricalAuthentication(isAuthenticatedSuccessfully);
} }
} else { });
_securitySettingsViewModel } else {
.setAllowBiometricalAuthentication(isAuthenticatedSuccessfully); _securitySettingsViewModel.setAllowBiometricalAuthentication(value);
} }
}); });
} else { }),
_securitySettingsViewModel.setAllowBiometricalAuthentication(value);
}
});
}),
Observer(builder: (_) { Observer(builder: (_) {
return SettingsPickerCell<PinCodeRequiredDuration>( return SettingsPickerCell<PinCodeRequiredDuration>(
title: S.current.require_pin_after, title: S.current.require_pin_after,

View file

@ -1,8 +1,7 @@
import 'package:cake_wallet/palette.dart'; import 'package:cake_wallet/palette.dart';
import 'package:cake_wallet/utils/responsive_layout_util.dart'; import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:cake_wallet/src/widgets/alert_background.dart'; import 'package:cake_wallet/src/widgets/picker_wrapper_widget.dart';
import 'package:cake_wallet/src/widgets/alert_close_button.dart';
class CheckBoxPicker extends StatefulWidget { class CheckBoxPicker extends StatefulWidget {
CheckBoxPicker({ CheckBoxPicker({
@ -32,73 +31,57 @@ class CheckBoxPickerState extends State<CheckBoxPicker> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return AlertBackground( return PickerWrapperWidget(
child: Column( children: [
children: [ if (widget.title.isNotEmpty)
Expanded( Container(
child: Stack( padding: EdgeInsets.symmetric(horizontal: 24),
alignment: Alignment.center, child: Text(
children: [ widget.title,
Column( 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, mainAxisSize: MainAxisSize.min,
children: <Widget>[ children: [
if (widget.title.isNotEmpty) Flexible(
Container( child: Stack(
padding: EdgeInsets.symmetric(horizontal: 24), alignment: Alignment.center,
child: Text( children: <Widget>[
widget.title, items.length > 3
textAlign: TextAlign.center, ? Scrollbar(
style: TextStyle( controller: controller,
fontSize: 18, child: itemsList(),
fontFamily: 'Lato', )
fontWeight: FontWeight.bold, : itemsList(),
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: <Widget>[
items.length > 3
? Scrollbar(
controller: controller,
child: itemsList(),
)
: itemsList(),
],
),
),
],
),
),
),
), ),
), ),
], ],
), ),
SizedBox(height: ResponsiveLayoutUtil.kPopupSpaceHeight), ),
AlertCloseButton(),
],
), ),
), ),
], ),
), ],
); );
} }
@ -111,7 +94,10 @@ class CheckBoxPickerState extends State<CheckBoxPicker> {
shrinkWrap: true, shrinkWrap: true,
separatorBuilder: (context, index) => widget.isSeparated separatorBuilder: (context, index) => widget.isSeparated
? Divider( ? Divider(
color: Theme.of(context).accentTextTheme.headline6!.backgroundColor!, color: Theme.of(context)
.accentTextTheme
.headline6!
.backgroundColor!,
height: 1, height: 1,
) )
: const SizedBox(), : const SizedBox(),

View file

@ -2,9 +2,8 @@
import 'package:cake_wallet/utils/responsive_layout_util.dart'; import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:flutter/material.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:cw_core/currency.dart';
import 'package:cake_wallet/src/widgets/picker_wrapper_widget.dart';
class Picker<Item> extends StatefulWidget { class Picker<Item> extends StatefulWidget {
Picker({ Picker({
@ -114,171 +113,130 @@ class _PickerState<Item> extends State<Picker<Item>> {
final mq = MediaQuery.of(context); final mq = MediaQuery.of(context);
final bottom = mq.viewInsets.bottom; final bottom = mq.viewInsets.bottom;
final height = mq.size.height - bottom; final height = mq.size.height - bottom;
final screenCenter = height / 2;
double closeButtonBottom = 60;
double containerHeight = height * 0.65; double containerHeight = height * 0.65;
if (bottom > 0) { if (bottom > 0) {
// increase a bit or it gets too squished in the top // increase a bit or it gets too squished in the top
containerHeight = height * 0.75; 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( return PickerWrapperWidget(
child: Column( hasTitle: widget.title?.isNotEmpty ?? false,
children: [ children: [
Expanded( if (widget.title?.isNotEmpty ?? false)
flex: 1, Container(
child: Stack( padding: EdgeInsets.symmetric(horizontal: padding),
alignment: Alignment.center, child: Text(
children: <Widget>[ widget.title!,
Column( textAlign: TextAlign.center,
mainAxisSize: MainAxisSize.min, style: TextStyle(
mainAxisAlignment: MainAxisAlignment.center, fontSize: 18,
children: <Widget>[ fontFamily: 'Lato',
if (widget.title?.isNotEmpty ?? false) fontWeight: FontWeight.bold,
Container( decoration: TextDecoration.none,
padding: EdgeInsets.symmetric(horizontal: padding), color: Colors.white,
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: <Widget>[
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),
],
), ),
), ),
// gives the extra spacing using MediaQuery.viewInsets.bottom Padding(
// to simulate a keyboard area padding: EdgeInsets.symmetric(horizontal: padding),
SizedBox( child: ClipRRect(
height: bottom, 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: <Widget>[
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(),
],
),
),
],
),
),
),
),
)
],
); );
} }

View file

@ -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<Widget> 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: <Widget>[
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,
)
],
),
);
}
}

View file

@ -34,6 +34,8 @@ abstract class SettingsStoreBase with Store {
required BalanceDisplayMode initialBalanceDisplayMode, required BalanceDisplayMode initialBalanceDisplayMode,
required bool initialSaveRecipientAddress, required bool initialSaveRecipientAddress,
required bool initialAppSecure, required bool initialAppSecure,
required bool initialDisableBuy,
required bool initialDisableSell,
required FiatApiMode initialFiatMode, required FiatApiMode initialFiatMode,
required bool initialAllowBiometricalAuthentication, required bool initialAllowBiometricalAuthentication,
required ExchangeApiMode initialExchangeStatus, required ExchangeApiMode initialExchangeStatus,
@ -57,6 +59,8 @@ abstract class SettingsStoreBase with Store {
balanceDisplayMode = initialBalanceDisplayMode, balanceDisplayMode = initialBalanceDisplayMode,
shouldSaveRecipientAddress = initialSaveRecipientAddress, shouldSaveRecipientAddress = initialSaveRecipientAddress,
isAppSecure = initialAppSecure, isAppSecure = initialAppSecure,
disableBuy = initialDisableBuy,
disableSell = initialDisableSell,
fiatApiMode = initialFiatMode, fiatApiMode = initialFiatMode,
allowBiometricalAuthentication = initialAllowBiometricalAuthentication, allowBiometricalAuthentication = initialAllowBiometricalAuthentication,
shouldShowMarketPlaceInDashboard = initialShouldShowMarketPlaceInDashboard, 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) { if (Platform.isAndroid) {
setIsAppSecureNative(isAppSecure); setIsAppSecureNative(isAppSecure);
} }
@ -217,6 +231,12 @@ abstract class SettingsStoreBase with Store {
@observable @observable
bool isAppSecure; bool isAppSecure;
@observable
bool disableBuy;
@observable
bool disableSell;
@observable @observable
bool allowBiometricalAuthentication; bool allowBiometricalAuthentication;
@ -309,6 +329,10 @@ abstract class SettingsStoreBase with Store {
sharedPreferences.getBool(PreferencesKey.shouldSaveRecipientAddressKey) ?? false; sharedPreferences.getBool(PreferencesKey.shouldSaveRecipientAddressKey) ?? false;
final isAppSecure = final isAppSecure =
sharedPreferences.getBool(PreferencesKey.isAppSecureKey) ?? false; sharedPreferences.getBool(PreferencesKey.isAppSecureKey) ?? false;
final disableBuy =
sharedPreferences.getBool(PreferencesKey.disableBuyKey) ?? false;
final disableSell =
sharedPreferences.getBool(PreferencesKey.disableSellKey) ?? false;
final currentFiatApiMode = FiatApiMode.deserialize( final currentFiatApiMode = FiatApiMode.deserialize(
raw: sharedPreferences raw: sharedPreferences
.getInt(PreferencesKey.currentFiatApiModeKey) ?? FiatApiMode.enabled.raw); .getInt(PreferencesKey.currentFiatApiModeKey) ?? FiatApiMode.enabled.raw);
@ -388,6 +412,8 @@ abstract class SettingsStoreBase with Store {
initialBalanceDisplayMode: currentBalanceDisplayMode, initialBalanceDisplayMode: currentBalanceDisplayMode,
initialSaveRecipientAddress: shouldSaveRecipientAddress, initialSaveRecipientAddress: shouldSaveRecipientAddress,
initialAppSecure: isAppSecure, initialAppSecure: isAppSecure,
initialDisableBuy: disableBuy,
initialDisableSell: disableSell,
initialFiatMode: currentFiatApiMode, initialFiatMode: currentFiatApiMode,
initialAllowBiometricalAuthentication: allowBiometricalAuthentication, initialAllowBiometricalAuthentication: allowBiometricalAuthentication,
initialExchangeStatus: exchangeStatus, initialExchangeStatus: exchangeStatus,
@ -435,6 +461,10 @@ abstract class SettingsStoreBase with Store {
sharedPreferences.getBool(PreferencesKey.shouldSaveRecipientAddressKey) ?? shouldSaveRecipientAddress; sharedPreferences.getBool(PreferencesKey.shouldSaveRecipientAddressKey) ?? shouldSaveRecipientAddress;
isAppSecure = isAppSecure =
sharedPreferences.getBool(PreferencesKey.isAppSecureKey) ?? isAppSecure; sharedPreferences.getBool(PreferencesKey.isAppSecureKey) ?? isAppSecure;
disableBuy =
sharedPreferences.getBool(PreferencesKey.disableBuyKey) ?? disableBuy;
disableSell =
sharedPreferences.getBool(PreferencesKey.disableSellKey) ?? disableSell;
allowBiometricalAuthentication = sharedPreferences allowBiometricalAuthentication = sharedPreferences
.getBool(PreferencesKey.allowBiometricalAuthenticationKey) ?? .getBool(PreferencesKey.allowBiometricalAuthenticationKey) ??
allowBiometricalAuthentication; allowBiometricalAuthentication;

View file

@ -141,9 +141,9 @@ class ExceptionHandler {
"errno = 103", // SocketException: Software caused connection abort "errno = 103", // SocketException: Software caused connection abort
"errno = 104", // SocketException: Connection reset by peer "errno = 104", // SocketException: Connection reset by peer
"errno = 110", // SocketException: Connection timed out "errno = 110", // SocketException: Connection timed out
"HttpException: Connection reset by peer", "Connection reset by peer",
"HttpException: Connection closed before full header was received", "Connection closed before full header was received",
"HandshakeException: Connection terminated during handshake", "Connection terminated during handshake",
"PERMISSION_NOT_GRANTED", "PERMISSION_NOT_GRANTED",
]; ];
@ -172,7 +172,7 @@ class ExceptionHandler {
} }
await file.writeAsString( await file.writeAsString(
"App Version: $currentVersion\n\nDevice Info $deviceInfo", "App Version: $currentVersion\n\nDevice Info $deviceInfo\n\n",
mode: FileMode.append, mode: FileMode.append,
); );
} }
@ -193,6 +193,7 @@ class ExceptionHandler {
'systemVersion': data.systemVersion, 'systemVersion': data.systemVersion,
'model': data.model, 'model': data.model,
'localizedModel': data.localizedModel, 'localizedModel': data.localizedModel,
'isPhysicalDevice': data.isPhysicalDevice,
}; };
} }

View file

@ -46,9 +46,7 @@ abstract class DashboardViewModelBase with Store {
required this.anonpayTransactionsStore}) required this.anonpayTransactionsStore})
: isOutdatedElectrumWallet = false, : isOutdatedElectrumWallet = false,
hasSellAction = false, hasSellAction = false,
isEnabledSellAction = false,
hasBuyAction = false, hasBuyAction = false,
isEnabledBuyAction = false,
hasExchangeAction = false, hasExchangeAction = false,
isShowFirstYatIntroduction = false, isShowFirstYatIntroduction = false,
isShowSecondYatIntroduction = false, isShowSecondYatIntroduction = false,
@ -287,14 +285,19 @@ abstract class DashboardViewModelBase with Store {
@observable @observable
bool hasExchangeAction; bool hasExchangeAction;
@observable @computed
bool isEnabledBuyAction; bool get isEnabledBuyAction =>
!settingsStore.disableBuy && wallet.type != WalletType.haven;
@observable @observable
bool hasBuyAction; bool hasBuyAction;
@observable @computed
bool isEnabledSellAction; bool get isEnabledSellAction =>
!settingsStore.disableSell &&
wallet.type != WalletType.haven &&
wallet.type != WalletType.monero &&
wallet.type != WalletType.litecoin;
@observable @observable
bool hasSellAction; bool hasSellAction;
@ -398,11 +401,7 @@ abstract class DashboardViewModelBase with Store {
void updateActions() { void updateActions() {
hasExchangeAction = !isHaven; hasExchangeAction = !isHaven;
isEnabledBuyAction = wallet.type != WalletType.haven;
hasBuyAction = !isHaven; hasBuyAction = !isHaven;
isEnabledSellAction = wallet.type != WalletType.haven
&& wallet.type != WalletType.monero
&& wallet.type != WalletType.litecoin;
hasSellAction = !isHaven; hasSellAction = !isHaven;
} }
} }

View file

@ -123,8 +123,8 @@ abstract class ExchangeTradeViewModelBase with Store {
} }
void _updateItems() { void _updateItems() {
final tagFrom = trade.from.tag != null ? '${trade.from.tag}' + ' ' : ''; final tagFrom = tradesStore.trade!.from.tag != null ? '${tradesStore.trade!.from.tag}' + ' ' : '';
final tagTo = trade.to.tag != null ? '${trade.to.tag}' + ' ' : ''; final tagTo = tradesStore.trade!.to.tag != null ? '${tradesStore.trade!.to.tag}' + ' ' : '';
items.clear(); items.clear();
items.add(ExchangeTradeItem( items.add(ExchangeTradeItem(
title: "${trade.provider.title} ${S.current.id}", data: '${trade.id}', isCopied: true)); title: "${trade.provider.title} ${S.current.id}", data: '${trade.id}', isCopied: true));
@ -142,11 +142,11 @@ abstract class ExchangeTradeViewModelBase with Store {
items.addAll([ items.addAll([
ExchangeTradeItem(title: S.current.amount, data: '${trade.amount}', isCopied: true), ExchangeTradeItem(title: S.current.amount, data: '${trade.amount}', isCopied: true),
ExchangeTradeItem( 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 ?? '', data: trade.inputAddress ?? '',
isCopied: true), isCopied: true),
ExchangeTradeItem( 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 ?? '', data: trade.payoutAddress ?? '',
isCopied: true), isCopied: true),
]); ]);

View file

@ -3,7 +3,6 @@ import 'dart:collection';
import 'dart:convert'; import 'dart:convert';
import 'package:cake_wallet/entities/exchange_api_mode.dart'; 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/entities/preferences_key.dart';
import 'package:cake_wallet/exchange/sideshift/sideshift_exchange_provider.dart'; import 'package:cake_wallet/exchange/sideshift/sideshift_exchange_provider.dart';
import 'package:cake_wallet/exchange/sideshift/sideshift_request.dart'; import 'package:cake_wallet/exchange/sideshift/sideshift_request.dart';
@ -244,7 +243,7 @@ abstract class ExchangeViewModelBase with Store {
List<CryptoCurrency> depositCurrencies; List<CryptoCurrency> depositCurrencies;
NumberFormat _cryptoNumberFormat; final NumberFormat _cryptoNumberFormat;
final SettingsStore _settingsStore; final SettingsStore _settingsStore;
@ -388,27 +387,36 @@ abstract class ExchangeViewModelBase with Store {
double? lowestMin = double.maxFinite; double? lowestMin = double.maxFinite;
double? highestMax = 0.0; double? highestMax = 0.0;
for (var provider in selectedProviders) { try {
/// if this provider is not valid for the current pair, skip it for (var provider in selectedProviders) {
if (!providersForCurrentPair().contains(provider)) { /// if this provider is not valid for the current pair, skip it
continue; 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;
} }
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) { if (lowestMin != double.maxFinite) {
@ -534,7 +542,7 @@ abstract class ExchangeViewModelBase with Store {
/// ///
/// this is because the limitation of the SplayTreeMap that /// this is because the limitation of the SplayTreeMap that
/// you can't modify it while iterating through it /// you can't modify it while iterating through it
Future.delayed(Duration(milliseconds: 500), createTrade); Future.delayed(Duration(milliseconds: 200), createTrade);
} }
} }

View file

@ -24,6 +24,12 @@ abstract class PrivacySettingsViewModelBase with Store {
@computed @computed
bool get isAppSecure => _settingsStore.isAppSecure; bool get isAppSecure => _settingsStore.isAppSecure;
@computed
bool get disableBuy => _settingsStore.disableBuy;
@computed
bool get disableSell => _settingsStore.disableSell;
@action @action
void setShouldSaveRecipientAddress(bool value) => _settingsStore.shouldSaveRecipientAddress = value; void setShouldSaveRecipientAddress(bool value) => _settingsStore.shouldSaveRecipientAddress = value;
@ -36,4 +42,10 @@ abstract class PrivacySettingsViewModelBase with Store {
@action @action
void setIsAppSecure(bool value) => _settingsStore.isAppSecure = value; void setIsAppSecure(bool value) => _settingsStore.isAppSecure = value;
@action
void setDisableBuy(bool value) => _settingsStore.disableBuy = value;
@action
void setDisableSell(bool value) => _settingsStore.disableSell = value;
} }

View file

@ -25,7 +25,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
CwMoneroPlugin.register(with: registry.registrar(forPlugin: "CwMoneroPlugin")) CwMoneroPlugin.register(with: registry.registrar(forPlugin: "CwMoneroPlugin"))
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin")) DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
DevicelocalePlugin.register(with: registry.registrar(forPlugin: "DevicelocalePlugin")) 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")) InAppReviewPlugin.register(with: registry.registrar(forPlugin: "InAppReviewPlugin"))
FLTPackageInfoPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlugin")) FLTPackageInfoPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))

View file

@ -23,7 +23,7 @@ PODS:
- FlutterMacOS - FlutterMacOS
- devicelocale (0.0.1): - devicelocale (0.0.1):
- FlutterMacOS - FlutterMacOS
- flutter_secure_storage_macos (3.3.1): - flutter_secure_storage_macos (6.1.1):
- FlutterMacOS - FlutterMacOS
- FlutterMacOS (1.0.0) - FlutterMacOS (1.0.0)
- in_app_review (0.2.0): - in_app_review (0.2.0):
@ -106,16 +106,16 @@ SPEC CHECKSUMS:
cw_monero: ec03de55a19c4a2b174ea687e0f4202edc716fa4 cw_monero: ec03de55a19c4a2b174ea687e0f4202edc716fa4
device_info_plus: 5401765fde0b8d062a2f8eb65510fb17e77cf07f device_info_plus: 5401765fde0b8d062a2f8eb65510fb17e77cf07f
devicelocale: 9f0f36ac651cabae2c33f32dcff4f32b61c38225 devicelocale: 9f0f36ac651cabae2c33f32dcff4f32b61c38225
flutter_secure_storage_macos: 6ceee8fbc7f484553ad17f79361b556259df89aa flutter_secure_storage_macos: d56e2d218c1130b262bef8b4a7d64f88d7f9c9ea
FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24
in_app_review: a850789fad746e89bce03d4aeee8078b45a53fd0 in_app_review: a850789fad746e89bce03d4aeee8078b45a53fd0
package_info: 6eba2fd8d3371dda2d85c8db6fe97488f24b74b2 package_info: 6eba2fd8d3371dda2d85c8db6fe97488f24b74b2
path_provider_foundation: c68054786f1b4f3343858c1e1d0caaded73f0be9 path_provider_foundation: eaf5b3e458fc0e5fbb9940fb09980e853fe058b8
platform_device_id: 3e414428f45df149bbbfb623e2c0ca27c545b763 platform_device_id: 3e414428f45df149bbbfb623e2c0ca27c545b763
platform_device_id_macos: f763bb55f088be804d61b96eb4710b8ab6598e94 platform_device_id_macos: f763bb55f088be804d61b96eb4710b8ab6598e94
Reachability: 33e18b67625424e47b6cde6d202dce689ad7af96 Reachability: 33e18b67625424e47b6cde6d202dce689ad7af96
share_plus_macos: 853ee48e7dce06b633998ca0735d482dd671ade4 share_plus_macos: 853ee48e7dce06b633998ca0735d482dd671ade4
shared_preferences_foundation: 986fc17f3d3251412d18b0265f9c64113a8c2472 shared_preferences_foundation: e2dae3258e06f44cc55f49d42024fd8dd03c590c
url_launcher_macos: 5335912b679c073563f29d89d33d10d459f95451 url_launcher_macos: 5335912b679c073563f29d89d33d10d459f95451
wakelock_macos: bc3f2a9bd8d2e6c89fee1e1822e7ddac3bd004a9 wakelock_macos: bc3f2a9bd8d2e6c89fee1e1822e7ddac3bd004a9

View file

@ -17,8 +17,8 @@ dependencies:
git: git:
url: https://github.com/cake-tech/flutter_secure_storage.git url: https://github.com/cake-tech/flutter_secure_storage.git
path: flutter_secure_storage path: flutter_secure_storage
ref: cake-6.0.0 ref: cake-8.0.0
version: 6.0.0 version: 8.0.0
# provider: ^6.0.3 # provider: ^6.0.3
rxdart: ^0.27.4 rxdart: ^0.27.4
yaml: ^3.1.1 yaml: ^3.1.1
@ -81,6 +81,10 @@ dependencies:
path_provider_android: 2.0.24 path_provider_android: 2.0.24
shared_preferences_android: 2.0.17 shared_preferences_android: 2.0.17
url_launcher_android: 6.0.24 url_launcher_android: 6.0.24
bitcoin_flutter:
git:
url: https://github.com/cake-tech/bitcoin_flutter.git
ref: cake-update-v2
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:

View file

@ -706,5 +706,7 @@
"error_text_input_below_minimum_limit":" المبلغ أقل من الحد الأدنى", "error_text_input_below_minimum_limit":" المبلغ أقل من الحد الأدنى",
"error_text_input_above_maximum_limit":"المبلغ أكبر من الحد الأقصى", "error_text_input_above_maximum_limit":"المبلغ أكبر من الحد الأقصى",
"show_market_place": "إظهار السوق", "show_market_place": "إظهار السوق",
"prevent_screenshots": "منع لقطات الشاشة وتسجيل الشاشة" "prevent_screenshots": "منع لقطات الشاشة وتسجيل الشاشة",
"disable_buy": "تعطيل إجراء الشراء",
"disable_sell": "قم بتعطيل إجراء البيع"
} }

View file

@ -702,5 +702,7 @@
"error_text_input_below_minimum_limit" : "Сумата е по-малко от минималната", "error_text_input_below_minimum_limit" : "Сумата е по-малко от минималната",
"error_text_input_above_maximum_limit" : "Сумата надвишава максималната", "error_text_input_above_maximum_limit" : "Сумата надвишава максималната",
"show_market_place":"Покажи пазар", "show_market_place":"Покажи пазар",
"prevent_screenshots": "Предотвратете екранни снимки и запис на екрана" "prevent_screenshots": "Предотвратете екранни снимки и запис на екрана",
"disable_buy": "Деактивирайте действието за покупка",
"disable_sell": "Деактивирайте действието за продажба"
} }

View file

@ -702,5 +702,7 @@
"error_text_input_below_minimum_limit" : "Částka je menší než minimální hodnota", "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", "error_text_input_above_maximum_limit" : "Částka je větší než maximální hodnota",
"show_market_place": "Zobrazit trh", "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"
} }

View file

@ -708,5 +708,7 @@
"error_text_input_below_minimum_limit" : "Menge ist unter dem Minimum", "error_text_input_below_minimum_limit" : "Menge ist unter dem Minimum",
"error_text_input_above_maximum_limit" : "Menge ist über dem Maximum", "error_text_input_above_maximum_limit" : "Menge ist über dem Maximum",
"show_market_place": "Marktplatz anzeigen", "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"
} }

View file

@ -708,5 +708,7 @@
"error_text_input_below_minimum_limit" : "Amount is less than the minimum", "error_text_input_below_minimum_limit" : "Amount is less than the minimum",
"error_text_input_above_maximum_limit" : "Amount is more than the maximum", "error_text_input_above_maximum_limit" : "Amount is more than the maximum",
"show_market_place" :"Show Marketplace", "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"
} }

View file

@ -708,5 +708,7 @@
"error_text_input_below_minimum_limit" : "La cantidad es menos que mínima", "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", "error_text_input_above_maximum_limit" : "La cantidad es más que el máximo",
"show_market_place": "Mostrar mercado", "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"
} }

View file

@ -708,5 +708,7 @@
"error_text_input_below_minimum_limit" : "Le montant est inférieur au minimum", "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", "error_text_input_above_maximum_limit" : "Le montant est supérieur au maximum",
"show_market_place" :"Afficher la place de marché", "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"
} }

View file

@ -708,5 +708,7 @@
"error_text_input_below_minimum_limit" : "राशि न्यूनतम से कम है", "error_text_input_below_minimum_limit" : "राशि न्यूनतम से कम है",
"error_text_input_above_maximum_limit" : "राशि अधिकतम से अधिक है", "error_text_input_above_maximum_limit" : "राशि अधिकतम से अधिक है",
"show_market_place":"बाज़ार दिखाएँ", "show_market_place":"बाज़ार दिखाएँ",
"prevent_screenshots": "स्क्रीनशॉट और स्क्रीन रिकॉर्डिंग रोकें" "prevent_screenshots": "स्क्रीनशॉट और स्क्रीन रिकॉर्डिंग रोकें",
"disable_buy": "खरीद कार्रवाई अक्षम करें",
"disable_sell": "बेचने की कार्रवाई अक्षम करें"
} }

View file

@ -708,5 +708,7 @@
"error_text_input_below_minimum_limit" : "Iznos je manji od minimalnog", "error_text_input_below_minimum_limit" : "Iznos je manji od minimalnog",
"error_text_input_above_maximum_limit" : "Iznos je veći od maskimalnog", "error_text_input_above_maximum_limit" : "Iznos je veći od maskimalnog",
"show_market_place" : "Prikaži tržište", "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"
} }

View file

@ -684,5 +684,7 @@
"error_text_input_below_minimum_limit" : "Jumlah kurang dari minimal", "error_text_input_below_minimum_limit" : "Jumlah kurang dari minimal",
"error_text_input_above_maximum_limit" : "Jumlah lebih dari maksimal", "error_text_input_above_maximum_limit" : "Jumlah lebih dari maksimal",
"show_market_place": "Tampilkan Pasar", "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"
} }

View file

@ -708,5 +708,7 @@
"error_text_input_below_minimum_limit" : "L'ammontare è inferiore al minimo", "error_text_input_below_minimum_limit" : "L'ammontare è inferiore al minimo",
"error_text_input_above_maximum_limit" : "L'ammontare è superiore al massimo", "error_text_input_above_maximum_limit" : "L'ammontare è superiore al massimo",
"show_market_place":"Mostra mercato", "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"
} }

View file

@ -708,5 +708,7 @@
"error_text_input_below_minimum_limit" : "金額は最小額より少ない", "error_text_input_below_minimum_limit" : "金額は最小額より少ない",
"error_text_input_above_maximum_limit" : "金額は最大値を超えています", "error_text_input_above_maximum_limit" : "金額は最大値を超えています",
"show_market_place":"マーケットプレイスを表示", "show_market_place":"マーケットプレイスを表示",
"prevent_screenshots": "スクリーンショットと画面録画を防止する" "prevent_screenshots": "スクリーンショットと画面録画を防止する",
"disable_buy": "購入アクションを無効にする",
"disable_sell": "販売アクションを無効にする"
} }

View file

@ -708,5 +708,7 @@
"error_text_input_below_minimum_limit" : "금액이 최소보다 적습니다.", "error_text_input_below_minimum_limit" : "금액이 최소보다 적습니다.",
"error_text_input_above_maximum_limit" : "금액이 최대 값보다 많습니다.", "error_text_input_above_maximum_limit" : "금액이 최대 값보다 많습니다.",
"show_market_place":"마켓플레이스 표시", "show_market_place":"마켓플레이스 표시",
"prevent_screenshots": "스크린샷 및 화면 녹화 방지" "prevent_screenshots": "스크린샷 및 화면 녹화 방지",
"disable_buy": "구매 행동 비활성화",
"disable_sell": "판매 조치 비활성화"
} }

View file

@ -708,5 +708,7 @@
"error_text_input_below_minimum_limit" : "ပမာဏသည် အနိမ့်ဆုံးထက်နည်းသည်။", "error_text_input_below_minimum_limit" : "ပမာဏသည် အနိမ့်ဆုံးထက်နည်းသည်။",
"error_text_input_above_maximum_limit" : "ပမာဏသည် အများဆုံးထက် ပိုများသည်။", "error_text_input_above_maximum_limit" : "ပမာဏသည် အများဆုံးထက် ပိုများသည်။",
"show_market_place":"စျေးကွက်ကိုပြသပါ။", "show_market_place":"စျေးကွက်ကိုပြသပါ။",
"prevent_screenshots": "ဖန်သားပြင်ဓာတ်ပုံများနှင့် မျက်နှာပြင်ရိုက်ကူးခြင်းကို တားဆီးပါ။" "prevent_screenshots": "ဖန်သားပြင်ဓာတ်ပုံများနှင့် မျက်နှာပြင်ရိုက်ကူးခြင်းကို တားဆီးပါ။",
"disable_buy": "ဝယ်ယူမှု လုပ်ဆောင်ချက်ကို ပိတ်ပါ။",
"disable_sell": "ရောင်းချခြင်းလုပ်ဆောင်ချက်ကို ပိတ်ပါ။"
} }

View file

@ -708,5 +708,7 @@
"error_text_input_below_minimum_limit" : "Bedrag is minder dan minimaal", "error_text_input_below_minimum_limit" : "Bedrag is minder dan minimaal",
"error_text_input_above_maximum_limit" : "Bedrag is meer dan maximaal", "error_text_input_above_maximum_limit" : "Bedrag is meer dan maximaal",
"show_market_place":"Toon Marktplaats", "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"
} }

View file

@ -708,5 +708,7 @@
"error_text_input_below_minimum_limit" : "Kwota jest mniejsza niż minimalna", "error_text_input_below_minimum_limit" : "Kwota jest mniejsza niż minimalna",
"error_text_input_above_maximum_limit" : "Kwota jest większa niż maksymalna", "error_text_input_above_maximum_limit" : "Kwota jest większa niż maksymalna",
"show_market_place" : "Pokaż rynek", "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"
} }

View file

@ -707,5 +707,7 @@
"error_text_input_below_minimum_limit" : "O valor é menor que o mínimo", "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", "error_text_input_above_maximum_limit" : "O valor é superior ao máximo",
"show_market_place":"Mostrar mercado", "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"
} }

View file

@ -708,5 +708,7 @@
"error_text_input_below_minimum_limit" : "Сумма меньше минимальной", "error_text_input_below_minimum_limit" : "Сумма меньше минимальной",
"error_text_input_above_maximum_limit" : "Сумма больше максимальной", "error_text_input_above_maximum_limit" : "Сумма больше максимальной",
"show_market_place":"Показать торговую площадку", "show_market_place":"Показать торговую площадку",
"prevent_screenshots": "Предотвратить скриншоты и запись экрана" "prevent_screenshots": "Предотвратить скриншоты и запись экрана",
"disable_buy": "Отключить действие покупки",
"disable_sell": "Отключить действие продажи"
} }

View file

@ -706,5 +706,7 @@
"error_text_input_below_minimum_limit" : "จำนวนเงินน้อยกว่าขั้นต่ำ", "error_text_input_below_minimum_limit" : "จำนวนเงินน้อยกว่าขั้นต่ำ",
"error_text_input_above_maximum_limit" : "จำนวนเงินสูงกว่าค่าสูงสุด", "error_text_input_above_maximum_limit" : "จำนวนเงินสูงกว่าค่าสูงสุด",
"show_market_place":"แสดงตลาดกลาง", "show_market_place":"แสดงตลาดกลาง",
"prevent_screenshots": "ป้องกันภาพหน้าจอและการบันทึกหน้าจอ" "prevent_screenshots": "ป้องกันภาพหน้าจอและการบันทึกหน้าจอ",
"disable_buy": "ปิดการใช้งานการซื้อ",
"disable_sell": "ปิดการใช้งานการขาย"
} }

View file

@ -708,5 +708,7 @@
"error_text_input_below_minimum_limit" : "Miktar minimumdan daha azdır", "error_text_input_below_minimum_limit" : "Miktar minimumdan daha azdır",
"error_text_input_above_maximum_limit" : "Miktar maksimumdan daha fazla", "error_text_input_above_maximum_limit" : "Miktar maksimumdan daha fazla",
"show_market_place":"Pazar Yerini Göster", "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"
} }

View file

@ -707,5 +707,7 @@
"error_text_input_below_minimum_limit" : "Сума менша мінімальної", "error_text_input_below_minimum_limit" : "Сума менша мінімальної",
"error_text_input_above_maximum_limit" : "Сума більше максимальної", "error_text_input_above_maximum_limit" : "Сума більше максимальної",
"show_market_place":"Відображати маркетплейс", "show_market_place":"Відображати маркетплейс",
"prevent_screenshots": "Запобігати знімкам екрана та запису екрана" "prevent_screenshots": "Запобігати знімкам екрана та запису екрана",
"disable_buy": "Вимкнути дію покупки",
"disable_sell": "Вимкнути дію продажу"
} }

View file

@ -703,5 +703,7 @@
"error_text_input_below_minimum_limit" : "رقم کم از کم سے کم ہے۔", "error_text_input_below_minimum_limit" : "رقم کم از کم سے کم ہے۔",
"error_text_input_above_maximum_limit" : "رقم زیادہ سے زیادہ سے زیادہ ہے۔", "error_text_input_above_maximum_limit" : "رقم زیادہ سے زیادہ سے زیادہ ہے۔",
"show_market_place":"بازار دکھائیں۔", "show_market_place":"بازار دکھائیں۔",
"prevent_screenshots": "اسکرین شاٹس اور اسکرین ریکارڈنگ کو روکیں۔" "prevent_screenshots": "اسکرین شاٹس اور اسکرین ریکارڈنگ کو روکیں۔",
"disable_buy": "خرید ایکشن کو غیر فعال کریں۔",
"disable_sell": "فروخت کی کارروائی کو غیر فعال کریں۔"
} }

View file

@ -707,5 +707,7 @@
"error_text_input_below_minimum_limit" : "金额小于最小值", "error_text_input_below_minimum_limit" : "金额小于最小值",
"error_text_input_above_maximum_limit" : "金额大于最大值", "error_text_input_above_maximum_limit" : "金额大于最大值",
"show_market_place" :"显示市场", "show_market_place" :"显示市场",
"prevent_screenshots": "防止截屏和录屏" "prevent_screenshots": "防止截屏和录屏",
"disable_buy": "禁用购买操作",
"disable_sell": "禁用卖出操作"
} }

View file

@ -14,14 +14,14 @@ TYPES=($MONERO_COM $CAKEWALLET $HAVEN)
APP_ANDROID_TYPE=$1 APP_ANDROID_TYPE=$1
MONERO_COM_NAME="Monero.com" MONERO_COM_NAME="Monero.com"
MONERO_COM_VERSION="1.3.5" MONERO_COM_VERSION="1.3.6"
MONERO_COM_BUILD_NUMBER=48 MONERO_COM_BUILD_NUMBER=49
MONERO_COM_BUNDLE_ID="com.monero.app" MONERO_COM_BUNDLE_ID="com.monero.app"
MONERO_COM_PACKAGE="com.monero.app" MONERO_COM_PACKAGE="com.monero.app"
CAKEWALLET_NAME="Cake Wallet" CAKEWALLET_NAME="Cake Wallet"
CAKEWALLET_VERSION="4.6.4" CAKEWALLET_VERSION="4.6.5"
CAKEWALLET_BUILD_NUMBER=158 CAKEWALLET_BUILD_NUMBER=159
CAKEWALLET_BUNDLE_ID="com.cakewallet.cake_wallet" CAKEWALLET_BUNDLE_ID="com.cakewallet.cake_wallet"
CAKEWALLET_PACKAGE="com.cakewallet.cake_wallet" CAKEWALLET_PACKAGE="com.cakewallet.cake_wallet"

View file

@ -13,13 +13,13 @@ TYPES=($MONERO_COM $CAKEWALLET $HAVEN)
APP_IOS_TYPE=$1 APP_IOS_TYPE=$1
MONERO_COM_NAME="Monero.com" MONERO_COM_NAME="Monero.com"
MONERO_COM_VERSION="1.3.5" MONERO_COM_VERSION="1.3.6"
MONERO_COM_BUILD_NUMBER=46 MONERO_COM_BUILD_NUMBER=47
MONERO_COM_BUNDLE_ID="com.cakewallet.monero" MONERO_COM_BUNDLE_ID="com.cakewallet.monero"
CAKEWALLET_NAME="Cake Wallet" CAKEWALLET_NAME="Cake Wallet"
CAKEWALLET_VERSION="4.6.4" CAKEWALLET_VERSION="4.6.5"
CAKEWALLET_BUILD_NUMBER=153 CAKEWALLET_BUILD_NUMBER=154
CAKEWALLET_BUNDLE_ID="com.fotolockr.cakewallet" CAKEWALLET_BUNDLE_ID="com.fotolockr.cakewallet"
HAVEN_NAME="Haven" HAVEN_NAME="Haven"

View file

@ -15,8 +15,8 @@ if [ -n "$1" ]; then
fi fi
CAKEWALLET_NAME="Cake Wallet" CAKEWALLET_NAME="Cake Wallet"
CAKEWALLET_VERSION="1.0.3" CAKEWALLET_VERSION="1.0.4"
CAKEWALLET_BUILD_NUMBER=20 CAKEWALLET_BUILD_NUMBER=22
CAKEWALLET_BUNDLE_ID="com.fotolockr.cakewallet" CAKEWALLET_BUNDLE_ID="com.fotolockr.cakewallet"
if ! [[ " ${TYPES[*]} " =~ " ${APP_MACOS_TYPE} " ]]; then if ! [[ " ${TYPES[*]} " =~ " ${APP_MACOS_TYPE} " ]]; then