diff --git a/android/app/src/main/AndroidManifestBase.xml b/android/app/src/main/AndroidManifestBase.xml
index 22278d5f1..9f6b65785 100644
--- a/android/app/src/main/AndroidManifestBase.xml
+++ b/android/app/src/main/AndroidManifestBase.xml
@@ -16,7 +16,7 @@
android:requestLegacyExternalStorage="true">
+
+
+
+
+
+
+
+
+
> feeRates() async {
try {
final topDoubleString = await estimatefee(p: 1);
- final middleDoubleString = await estimatefee(p: 20);
+ final middleDoubleString = await estimatefee(p: 5);
final bottomDoubleString = await estimatefee(p: 100);
final top =
(stringDoubleToBitcoinAmount(topDoubleString.toString()) / 1000)
diff --git a/cw_core/lib/monero_transaction_priority.dart b/cw_core/lib/monero_transaction_priority.dart
index cca887398..f5c00ecc7 100644
--- a/cw_core/lib/monero_transaction_priority.dart
+++ b/cw_core/lib/monero_transaction_priority.dart
@@ -21,22 +21,6 @@ class MoneroTransactionPriority extends TransactionPriority {
static const fastest = MoneroTransactionPriority(title: 'Fastest', raw: 4);
static const standard = slow;
-
- static List forWalletType(WalletType type) {
- switch (type) {
- case WalletType.monero:
- return MoneroTransactionPriority.all;
- case WalletType.bitcoin:
- return [
- MoneroTransactionPriority.slow,
- MoneroTransactionPriority.automatic,
- MoneroTransactionPriority.fast
- ];
- default:
- return [];
- }
- }
-
static MoneroTransactionPriority deserialize({required int raw}) {
switch (raw) {
case 0:
diff --git a/cw_core/lib/node.dart b/cw_core/lib/node.dart
index ff1d6b051..1322c5b78 100644
--- a/cw_core/lib/node.dart
+++ b/cw_core/lib/node.dart
@@ -53,7 +53,7 @@ class Node extends HiveObject with Keyable {
@HiveField(4)
bool? useSSL;
- @HiveField(5)
+ @HiveField(5, defaultValue: false)
bool trusted;
bool get isSSL => useSSL ?? false;
diff --git a/ios/Runner/InfoBase.plist b/ios/Runner/InfoBase.plist
index ad8816ca3..794391665 100644
--- a/ios/Runner/InfoBase.plist
+++ b/ios/Runner/InfoBase.plist
@@ -21,18 +21,48 @@
CFBundleSignature
????
CFBundleURLTypes
-
-
- CFBundleTypeRole
- Editor
- CFBundleURLName
- y.at
- CFBundleURLSchemes
-
- cakewallet
-
-
-
+
+
+ CFBundleTypeRole
+ Editor
+ CFBundleURLName
+ y.at
+ CFBundleURLSchemes
+
+ cakewallet
+
+
+
+ CFBundleTypeRole
+ Editor
+ CFBundleURLName
+ bitcoin
+ CFBundleURLSchemes
+
+ bitcoin
+
+
+
+ CFBundleTypeRole
+ Editor
+ CFBundleURLName
+ monero
+ CFBundleURLSchemes
+
+ monero
+
+
+
+ CFBundleTypeRole
+ Editor
+ CFBundleURLName
+ litecoin
+ CFBundleURLSchemes
+
+ litecoin
+
+
+
CFBundleVersion
$(CURRENT_PROJECT_VERSION)
LSRequiresIPhoneOS
diff --git a/lib/core/auth_service.dart b/lib/core/auth_service.dart
index 2ae37e2b0..54f89437a 100644
--- a/lib/core/auth_service.dart
+++ b/lib/core/auth_service.dart
@@ -4,12 +4,19 @@ import 'package:shared_preferences/shared_preferences.dart';
import 'package:cake_wallet/entities/preferences_key.dart';
import 'package:cake_wallet/entities/secret_store_key.dart';
import 'package:cake_wallet/entities/encrypt.dart';
+import 'package:cake_wallet/di.dart';
+import 'package:cake_wallet/store/settings_store.dart';
class AuthService with Store {
- AuthService({required this.secureStorage, required this.sharedPreferences});
+ AuthService({
+ required this.secureStorage,
+ required this.sharedPreferences,
+ required this.settingsStore,
+ });
final FlutterSecureStorage secureStorage;
final SharedPreferences sharedPreferences;
+ final SettingsStore settingsStore;
Future setPassword(String password) async {
final key = generateStoreKeyFor(key: SecretStoreKey.pinCodePassword);
@@ -19,8 +26,7 @@ class AuthService with Store {
Future canAuthenticate() async {
final key = generateStoreKeyFor(key: SecretStoreKey.pinCodePassword);
- final walletName =
- sharedPreferences.getString(PreferencesKey.currentWalletName) ?? '';
+ final walletName = sharedPreferences.getString(PreferencesKey.currentWalletName) ?? '';
var password = '';
try {
@@ -39,4 +45,25 @@ class AuthService with Store {
return decodedPin == pin;
}
+
+ void saveLastAuthTime() {
+ int timestamp = DateTime.now().millisecondsSinceEpoch;
+ sharedPreferences.setInt(PreferencesKey.lastAuthTimeMilliseconds, timestamp);
+ }
+
+ bool requireAuth() {
+ final timestamp = sharedPreferences.getInt(PreferencesKey.lastAuthTimeMilliseconds);
+ final duration = _durationToRequireAuth(timestamp ?? 0);
+ final requiredPinInterval = settingsStore.pinTimeOutDuration;
+
+ return duration >= requiredPinInterval.value;
+ }
+
+ int _durationToRequireAuth(int timestamp) {
+ DateTime before = DateTime.fromMillisecondsSinceEpoch(timestamp);
+ DateTime now = DateTime.now();
+ Duration timeDifference = now.difference(before);
+
+ return timeDifference.inMinutes;
+ }
}
diff --git a/lib/di.dart b/lib/di.dart
index 1febcd9e5..1acdad2dd 100644
--- a/lib/di.dart
+++ b/lib/di.dart
@@ -13,6 +13,7 @@ import 'package:cake_wallet/src/screens/ionia/cards/ionia_custom_redeem_page.dar
import 'package:cake_wallet/src/screens/ionia/cards/ionia_gift_card_detail_page.dart';
import 'package:cake_wallet/src/screens/ionia/cards/ionia_more_options_page.dart';
import 'package:cake_wallet/src/screens/settings/connection_sync_page.dart';
+import 'package:cake_wallet/utils/payment_request.dart';
import 'package:cake_wallet/view_model/ionia/ionia_auth_view_model.dart';
import 'package:cake_wallet/view_model/ionia/ionia_buy_card_view_model.dart';
import 'package:cake_wallet/view_model/ionia/ionia_custom_tip_view_model.dart';
@@ -308,7 +309,10 @@ Future setup(
getIt.registerFactory(() => AuthService(
secureStorage: getIt.get(),
- sharedPreferences: getIt.get()));
+ sharedPreferences: getIt.get(),
+ settingsStore: getIt.get(),
+ ),
+ );
getIt.registerFactory(() => AuthViewModel(
getIt.get(),
@@ -384,8 +388,11 @@ Future setup(
getIt.get(),
_transactionDescriptionBox));
- getIt.registerFactory(
- () => SendPage(sendViewModel: getIt.get()));
+ getIt.registerFactoryParam(
+ (PaymentRequest? initialPaymentRequest, _) => SendPage(
+ sendViewModel: getIt.get(),
+ initialPaymentRequest: initialPaymentRequest,
+ ));
getIt.registerFactory(() => SendTemplatePage(
sendTemplateViewModel: getIt.get()));
@@ -393,7 +400,10 @@ Future setup(
getIt.registerFactory(() => WalletListViewModel(
_walletInfoSource,
getIt.get(),
- getIt.get()));
+ getIt.get(),
+ getIt.get(),
+ ),
+ );
getIt.registerFactory(() =>
WalletListPage(walletListViewModel: getIt.get()));
@@ -453,7 +463,7 @@ Future setup(
});
getIt.registerFactory(() {
- return SecuritySettingsViewModel(getIt.get());
+ return SecuritySettingsViewModel(getIt.get(), getIt.get());
});
getIt
@@ -785,7 +795,8 @@ Future setup(
return IoniaMoreOptionsPage(giftCard);
});
- getIt.registerFactoryParam((IoniaGiftCard giftCard, _) => IoniaCustomRedeemViewModel(giftCard));
+ getIt.registerFactoryParam((IoniaGiftCard giftCard, _)
+ => IoniaCustomRedeemViewModel(giftCard: giftCard, ioniaService: getIt.get()));
getIt.registerFactoryParam((List args, _){
final giftCard = args.first as IoniaGiftCard;
@@ -823,4 +834,4 @@ Future setup(
AdvancedPrivacySettingsViewModel(type, getIt.get()));
_isSetupFinished = true;
-}
+}
\ No newline at end of file
diff --git a/lib/entities/default_settings_migration.dart b/lib/entities/default_settings_migration.dart
index 9aff05500..3c8d9fbbe 100644
--- a/lib/entities/default_settings_migration.dart
+++ b/lib/entities/default_settings_migration.dart
@@ -139,6 +139,10 @@ Future defaultSettingsMigration(
await addOnionNode(nodes);
break;
+ case 19:
+ await validateBitcoinSavedTransactionPriority(sharedPreferences);
+ break;
+
default:
break;
}
@@ -154,6 +158,18 @@ Future defaultSettingsMigration(
PreferencesKey.currentDefaultSettingsMigrationVersion, version);
}
+Future validateBitcoinSavedTransactionPriority(SharedPreferences sharedPreferences) async {
+ if (bitcoin == null) {
+ return;
+ }
+ final int? savedBitcoinPriority =
+ sharedPreferences.getInt(PreferencesKey.bitcoinTransactionPriority);
+ if (!bitcoin!.getTransactionPriorities().any((element) => element.raw == savedBitcoinPriority)) {
+ await sharedPreferences.setInt(
+ PreferencesKey.bitcoinTransactionPriority, bitcoin!.getMediumTransactionPriority().serialize());
+ }
+}
+
Future addOnionNode(Box nodes) async {
final onionNodeUri = "cakexmrl7bonq7ovjka5kuwuyd3f7qnkz6z6s6dmsy3uckwra7bvggyd.onion:18081";
diff --git a/lib/entities/pin_code_required_duration.dart b/lib/entities/pin_code_required_duration.dart
new file mode 100644
index 000000000..fef5715b5
--- /dev/null
+++ b/lib/entities/pin_code_required_duration.dart
@@ -0,0 +1,32 @@
+import 'package:cake_wallet/generated/i18n.dart';
+
+enum PinCodeRequiredDuration {
+ always(0),
+ tenminutes(10),
+ onehour(60);
+
+ const PinCodeRequiredDuration(this.value);
+ final int value;
+
+ static PinCodeRequiredDuration deserialize({required int raw}) =>
+ PinCodeRequiredDuration.values.firstWhere((e) => e.value == raw);
+
+ @override
+ String toString(){
+ String label = '';
+ switch (this) {
+ case PinCodeRequiredDuration.always:
+ label = S.current.always;
+ break;
+ case PinCodeRequiredDuration.tenminutes:
+ label = S.current.minutes_to_pin_code('10');
+ break;
+ case PinCodeRequiredDuration.onehour:
+ label = S.current.minutes_to_pin_code('60');
+ break;
+ }
+ return label;
+
+ }
+
+}
\ No newline at end of file
diff --git a/lib/entities/preferences_key.dart b/lib/entities/preferences_key.dart
index 6d9e93de6..90d4dcad7 100644
--- a/lib/entities/preferences_key.dart
+++ b/lib/entities/preferences_key.dart
@@ -27,6 +27,9 @@ class PreferencesKey {
static const shouldShowReceiveWarning = 'should_show_receive_warning';
static const shouldShowYatPopup = 'should_show_yat_popup';
static const moneroWalletPasswordUpdateV1Base = 'monero_wallet_update_v1';
+ static const pinTimeOutDuration = 'pin_timeout_duration';
+ static const lastAuthTimeMilliseconds = 'last_auth_time_milliseconds';
+
static String moneroWalletUpdateV1Key(String name)
=> '${PreferencesKey.moneroWalletPasswordUpdateV1Base}_${name}';
diff --git a/lib/ionia/ionia_gift_card.dart b/lib/ionia/ionia_gift_card.dart
index b729e261f..2ba0fd136 100644
--- a/lib/ionia/ionia_gift_card.dart
+++ b/lib/ionia/ionia_gift_card.dart
@@ -37,7 +37,7 @@ class IoniaGiftCard {
purchaseAmount: element['PurchaseAmount'] as double,
actualAmount: element['ActualAmount'] as double,
totalTransactionAmount: element['TotalTransactionAmount'] as double,
- totalDashTransactionAmount: element['TotalDashTransactionAmount'] as double,
+ totalDashTransactionAmount: (element['TotalDashTransactionAmount'] as double?) ?? 0.0,
remainingAmount: element['RemainingAmount'] as double,
isActive: element['IsActive'] as bool,
isEmpty: element['IsEmpty'] as bool,
diff --git a/lib/ionia/ionia_service.dart b/lib/ionia/ionia_service.dart
index 942bc25b5..51e23ad28 100644
--- a/lib/ionia/ionia_service.dart
+++ b/lib/ionia/ionia_service.dart
@@ -148,8 +148,8 @@ class IoniaService {
// Redeem
- Future redeem(IoniaGiftCard giftCard) async {
- await chargeGiftCard(giftCardId: giftCard.id, amount: giftCard.remainingAmount);
+ Future redeem({required int giftCardId, required double amount}) async {
+ await chargeGiftCard(giftCardId: giftCardId, amount: amount);
}
// Get Gift Card
diff --git a/lib/main.dart b/lib/main.dart
index 11eee146b..19f89feb3 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -1,5 +1,6 @@
import 'dart:async';
import 'package:cake_wallet/bitcoin/bitcoin.dart';
+import 'package:cake_wallet/core/auth_service.dart';
import 'package:cake_wallet/entities/language_service.dart';
import 'package:cake_wallet/buy/order.dart';
import 'package:cake_wallet/ionia/ionia_category.dart';
@@ -128,7 +129,7 @@ Future main() async {
exchangeTemplates: exchangeTemplates,
transactionDescriptions: transactionDescriptions,
secureStorage: secureStorage,
- initialMigrationVersion: 18);
+ initialMigrationVersion: 19);
runApp(App());
} catch (e, stacktrace) {
runApp(MaterialApp(
@@ -204,12 +205,6 @@ class AppState extends State with SingleTickerProviderStateMixin {
//_handleInitialUri();
}
- @override
- void dispose() {
- stream?.cancel();
- super.dispose();
- }
-
Future _handleInitialUri() async {
try {
final uri = await getInitialUri();
@@ -257,6 +252,7 @@ class AppState extends State with SingleTickerProviderStateMixin {
Widget build(BuildContext context) {
return Observer(builder: (BuildContext context) {
final appStore = getIt.get();
+ final authService = getIt.get();
final settingsStore = appStore.settingsStore;
final statusBarColor = Colors.transparent;
final authenticationStore = getIt.get();
@@ -281,6 +277,7 @@ class AppState extends State with SingleTickerProviderStateMixin {
appStore: appStore,
authenticationStore: authenticationStore,
navigatorKey: navigatorKey,
+ authService: authService,
child: MaterialApp(
navigatorKey: navigatorKey,
debugShowCheckedModeBanner: false,
diff --git a/lib/router.dart b/lib/router.dart
index b59294428..896ddd99b 100644
--- a/lib/router.dart
+++ b/lib/router.dart
@@ -25,6 +25,7 @@ import 'package:cake_wallet/src/screens/settings/connection_sync_page.dart';
import 'package:cake_wallet/src/screens/support/support_page.dart';
import 'package:cake_wallet/src/screens/unspent_coins/unspent_coins_details_page.dart';
import 'package:cake_wallet/src/screens/unspent_coins/unspent_coins_list_page.dart';
+import 'package:cake_wallet/utils/payment_request.dart';
import 'package:cake_wallet/view_model/monero_account_list/account_list_item.dart';
import 'package:cake_wallet/view_model/node_list/node_create_or_edit_view_model.dart';
import 'package:cake_wallet/view_model/advanced_privacy_settings_view_model.dart';
@@ -216,8 +217,12 @@ Route createRoute(RouteSettings settings) {
builder: (_) => getIt.get());
case Routes.send:
+ final initialPaymentRequest = settings.arguments as PaymentRequest?;
+
return CupertinoPageRoute(
- fullscreenDialog: true, builder: (_) => getIt.get());
+ fullscreenDialog: true, builder: (_) => getIt.get(
+ param1: initialPaymentRequest,
+ ));
case Routes.sendTemplate:
return CupertinoPageRoute(
diff --git a/lib/src/screens/dashboard/wallet_menu.dart b/lib/src/screens/dashboard/wallet_menu.dart
index bc1f20a5d..1638c0bc4 100644
--- a/lib/src/screens/dashboard/wallet_menu.dart
+++ b/lib/src/screens/dashboard/wallet_menu.dart
@@ -21,6 +21,12 @@ class WalletMenu {
height: 16, width: 16),
handler: () => Navigator.of(context).pushNamed(Routes.walletList),
),
+ WalletMenuItem(
+ title: S.current.address_book_menu,
+ image: Image.asset('assets/images/open_book_menu.png',
+ height: 16, width: 16),
+ handler: () => Navigator.of(context).pushNamed(Routes.addressBook),
+ ),
WalletMenuItem(
title: S.current.security_and_backup,
image:
@@ -29,18 +35,12 @@ class WalletMenu {
Navigator.of(context).pushNamed(Routes.securityBackupPage);
}),
WalletMenuItem(
- title: S.current.privacy,
+ title: S.current.privacy_settings,
image:
- Image.asset('assets/images/eye_menu.png', height: 16, width: 16),
+ Image.asset('assets/images/privacy_menu.png', height: 16, width: 16),
handler: () {
Navigator.of(context).pushNamed(Routes.privacyPage);
}),
- WalletMenuItem(
- title: S.current.address_book_menu,
- image: Image.asset('assets/images/open_book_menu.png',
- height: 16, width: 16),
- handler: () => Navigator.of(context).pushNamed(Routes.addressBook),
- ),
WalletMenuItem(
title: S.current.display_settings,
image: Image.asset('assets/images/eye_menu.png',
diff --git a/lib/src/screens/ionia/cards/ionia_custom_redeem_page.dart b/lib/src/screens/ionia/cards/ionia_custom_redeem_page.dart
index dae5413a4..f2702d791 100644
--- a/lib/src/screens/ionia/cards/ionia_custom_redeem_page.dart
+++ b/lib/src/screens/ionia/cards/ionia_custom_redeem_page.dart
@@ -1,3 +1,4 @@
+import 'package:cake_wallet/core/execution_state.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/screens/ionia/widgets/card_item.dart';
import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
@@ -18,12 +19,11 @@ class IoniaCustomRedeemPage extends BasePage {
) : _amountFieldFocus = FocusNode(),
_amountController = TextEditingController() {
_amountController.addListener(() {
- ioniaCustomRedeemViewModel.updateAmount(_amountController.text);
+ ioniaCustomRedeemViewModel.updateAmount(_amountController.text);
});
}
final IoniaCustomRedeemViewModel ioniaCustomRedeemViewModel;
-
@override
String get title => S.current.custom_redeem_amount;
@@ -50,7 +50,7 @@ class IoniaCustomRedeemPage extends BasePage {
disableScroll: true,
config: KeyboardActionsConfig(
keyboardActionsPlatform: KeyboardActionsPlatform.IOS,
- keyboardBarColor: Theme.of(context).accentTextTheme!.bodyText1!.backgroundColor!,
+ keyboardBarColor: Theme.of(context).accentTextTheme.bodyText1!.backgroundColor!,
nextFocus: false,
actions: [
KeyboardActionsItem(
@@ -67,10 +67,11 @@ class IoniaCustomRedeemPage extends BasePage {
Container(
padding: EdgeInsets.symmetric(horizontal: 25),
decoration: BoxDecoration(
- borderRadius: BorderRadius.only(bottomLeft: Radius.circular(24), bottomRight: Radius.circular(24)),
+ borderRadius: BorderRadius.only(
+ bottomLeft: Radius.circular(24), bottomRight: Radius.circular(24)),
gradient: LinearGradient(colors: [
- Theme.of(context).primaryTextTheme!.subtitle1!.color!,
- Theme.of(context).primaryTextTheme!.subtitle1!.decorationColor!,
+ Theme.of(context).primaryTextTheme.subtitle1!.color!,
+ Theme.of(context).primaryTextTheme.subtitle1!.decorationColor!,
], begin: Alignment.topLeft, end: Alignment.bottomRight),
),
child: Column(
@@ -85,11 +86,11 @@ class IoniaCustomRedeemPage extends BasePage {
inputFormatters: [FilteringTextInputFormatter.deny(RegExp('[\-|\ ]'))],
hintText: '1000',
placeholderTextStyle: TextStyle(
- color: Theme.of(context).primaryTextTheme!.headline5!.color!,
+ color: Theme.of(context).primaryTextTheme.headline5!.color!,
fontWeight: FontWeight.w500,
fontSize: 36,
),
- borderColor: Theme.of(context).primaryTextTheme!.headline5!.color!,
+ borderColor: Theme.of(context).primaryTextTheme.headline5!.color!,
textColor: Colors.white,
textStyle: TextStyle(
color: Colors.white,
@@ -114,14 +115,17 @@ class IoniaCustomRedeemPage extends BasePage {
),
),
SizedBox(height: 8),
- Observer(builder: (_)=>
- !ioniaCustomRedeemViewModel.disableRedeem ?
- Center(
- child: Text('\$${giftCard.remainingAmount} - \$${ioniaCustomRedeemViewModel.amount} = \$${ioniaCustomRedeemViewModel.formattedRemaining} ${S.of(context).remaining}',
- style: TextStyle(
- color: Theme.of(context).primaryTextTheme!.headline5!.color!,
- ),),
- ) : SizedBox.shrink(),
+ Observer(
+ builder: (_) => !ioniaCustomRedeemViewModel.disableRedeem
+ ? Center(
+ child: Text(
+ '\$${giftCard.remainingAmount} - \$${ioniaCustomRedeemViewModel.amount} = \$${ioniaCustomRedeemViewModel.formattedRemaining} ${S.of(context).remaining}',
+ style: TextStyle(
+ color: Theme.of(context).primaryTextTheme.headline5!.color!,
+ ),
+ ),
+ )
+ : SizedBox.shrink(),
),
SizedBox(height: 24),
],
@@ -131,30 +135,37 @@ class IoniaCustomRedeemPage extends BasePage {
padding: const EdgeInsets.all(24.0),
child: CardItem(
title: giftCard.legalName,
- backgroundColor: Theme.of(context).accentTextTheme!.headline1!.backgroundColor!.withOpacity(0.1),
+ backgroundColor: Theme.of(context)
+ .accentTextTheme
+ .headline1!
+ .backgroundColor!
+ .withOpacity(0.1),
discount: giftCard.remainingAmount,
isAmount: true,
discountBackground: AssetImage('assets/images/red_badge_discount.png'),
- titleColor: Theme.of(context).accentTextTheme!.headline1!.backgroundColor!,
+ titleColor: Theme.of(context).accentTextTheme.headline1!.backgroundColor!,
subtitleColor: Theme.of(context).hintColor,
subTitle: S.of(context).online,
logoUrl: giftCard.logoUrl,
),
- ),
+ ),
],
),
bottomSection: Column(
children: [
- Padding(
- padding: EdgeInsets.only(bottom: 12),
- child: PrimaryButton(
- onPressed: () {
- Navigator.of(context).pop(_amountController.text);
- },
- isDisabled: ioniaCustomRedeemViewModel.disableRedeem,
- text: S.of(context).add_custom_redemption,
- color: Theme.of(context).accentTextTheme!.bodyText1!.color!,
- textColor: Colors.white,
+ Observer(
+ builder: (_) => Padding(
+ padding: EdgeInsets.only(bottom: 12),
+ child: LoadingPrimaryButton(
+ isLoading: ioniaCustomRedeemViewModel.redeemState is IsExecutingState,
+ isDisabled: ioniaCustomRedeemViewModel.disableRedeem,
+ text: S.of(context).add_custom_redemption,
+ color: Theme.of(context).accentTextTheme.bodyText1!.color!,
+ textColor: Colors.white,
+ onPressed: () => ioniaCustomRedeemViewModel.addCustomRedeem().then((value) {
+ Navigator.of(context).pop(ioniaCustomRedeemViewModel.remaining.toString());
+ }),
+ ),
),
),
SizedBox(height: 30),
diff --git a/lib/src/screens/ionia/cards/ionia_gift_card_detail_page.dart b/lib/src/screens/ionia/cards/ionia_gift_card_detail_page.dart
index 1c768fa17..5b3c980ec 100644
--- a/lib/src/screens/ionia/cards/ionia_gift_card_detail_page.dart
+++ b/lib/src/screens/ionia/cards/ionia_gift_card_detail_page.dart
@@ -32,7 +32,7 @@ class IoniaGiftCardDetailPage extends BasePage {
final _backButton = Icon(
Icons.arrow_back_ios,
- color: Theme.of(context).primaryTextTheme!.headline6!.color!,
+ color: Theme.of(context).primaryTextTheme.headline6!.color!,
size: 16,
);
return Padding(
@@ -43,7 +43,7 @@ class IoniaGiftCardDetailPage extends BasePage {
child: ButtonTheme(
minWidth: double.minPositive,
child: TextButton(
- // FIX-ME: Style
+ // FIX-ME: Style
//highlightColor: Colors.transparent,
//splashColor: Colors.transparent,
//padding: EdgeInsets.all(0),
@@ -61,7 +61,8 @@ class IoniaGiftCardDetailPage extends BasePage {
Widget middle(BuildContext context) {
return Text(
viewModel.giftCard.legalName,
- style: textMediumSemiBold(color: Theme.of(context).accentTextTheme!.headline1!.backgroundColor!),
+ style:
+ textMediumSemiBold(color: Theme.of(context).accentTextTheme.headline1!.backgroundColor!),
);
}
@@ -102,20 +103,21 @@ class IoniaGiftCardDetailPage extends BasePage {
title: S.of(context).gift_card_number,
subTitle: viewModel.giftCard.cardNumber,
),
- if (viewModel.giftCard.cardPin?.isNotEmpty ?? false)
- ...[Divider(height: 30),
+ if (viewModel.giftCard.cardPin.isNotEmpty) ...[
+ Divider(height: 30),
buildIoniaTile(
context,
title: S.of(context).pin_number,
subTitle: viewModel.giftCard.cardPin,
- )],
+ )
+ ],
Divider(height: 30),
- Observer(builder: (_) =>
- buildIoniaTile(
- context,
- title: S.of(context).amount,
- subTitle: viewModel.remainingAmount.toStringAsFixed(2) ?? '0.00',
- )),
+ Observer(
+ builder: (_) => buildIoniaTile(
+ context,
+ title: S.of(context).amount,
+ subTitle: viewModel.remainingAmount.toStringAsFixed(2),
+ )),
Divider(height: 50),
TextIconButton(
label: S.of(context).how_to_use_card,
@@ -130,29 +132,28 @@ class IoniaGiftCardDetailPage extends BasePage {
if (!viewModel.giftCard.isEmpty) {
return Column(
children: [
- //PrimaryButton(
- // onPressed: () async {
- // final amount = await Navigator.of(context)
- // .pushNamed(Routes.ioniaMoreOptionsPage, arguments: [viewModel.giftCard]) as String;
- // if (amount != null) {
- // viewModel.updateRemaining(double.parse(amount));
- // }
- // },
- // text: S.of(context).more_options,
- // color: Theme.of(context).accentTextTheme!.caption!.color!,
- // textColor: Theme.of(context).primaryTextTheme!.headline6!.color!,
- //),
- //SizedBox(height: 12),
+ PrimaryButton(
+ onPressed: () async {
+ await Navigator.of(context).pushNamed(
+ Routes.ioniaMoreOptionsPage,
+ arguments: [viewModel.giftCard]) as String?;
+ viewModel.refeshCard();
+ },
+ text: S.of(context).more_options,
+ color: Theme.of(context).accentTextTheme.caption!.color!,
+ textColor: Theme.of(context).primaryTextTheme.headline6!.color!,
+ ),
+ SizedBox(height: 12),
LoadingPrimaryButton(
isLoading: viewModel.redeemState is IsExecutingState,
onPressed: () => viewModel.redeem().then(
(_) {
- Navigator.of(context)
- .pushNamedAndRemoveUntil(Routes.ioniaManageCardsPage, (route) => route.isFirst);
+ Navigator.of(context).pushNamedAndRemoveUntil(
+ Routes.ioniaManageCardsPage, (route) => route.isFirst);
},
),
text: S.of(context).mark_as_redeemed,
- color: Theme.of(context).accentTextTheme!.bodyText1!.color!,
+ color: Theme.of(context).accentTextTheme.bodyText1!.color!,
textColor: Colors.white,
),
],
@@ -168,12 +169,11 @@ class IoniaGiftCardDetailPage extends BasePage {
Widget buildIoniaTile(BuildContext context, {required String title, required String subTitle}) {
return IoniaTile(
- title: title,
- subTitle: subTitle,
- onTap: () {
- Clipboard.setData(ClipboardData(text: subTitle));
- showBar(context,
- S.of(context).transaction_details_copied(title));
+ title: title,
+ subTitle: subTitle,
+ onTap: () {
+ Clipboard.setData(ClipboardData(text: subTitle));
+ showBar(context, S.of(context).transaction_details_copied(title));
});
}
@@ -184,10 +184,10 @@ class IoniaGiftCardDetailPage extends BasePage {
showPopUp(
context: context,
builder: (BuildContext context) {
- return IoniaAlertModal(
+ return IoniaAlertModal(
title: S.of(context).how_to_use_card,
content: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
+ crossAxisAlignment: CrossAxisAlignment.start,
children: viewModel.giftCard.instructions
.map((instruction) {
return [
@@ -196,13 +196,13 @@ class IoniaGiftCardDetailPage extends BasePage {
child: Text(
instruction.header,
style: textLargeSemiBold(
- color: Theme.of(context).textTheme!.headline3!.color!,
+ color: Theme.of(context).textTheme.headline3!.color!,
),
)),
Text(
instruction.body,
style: textMedium(
- color: Theme.of(context).textTheme!.headline3!.color!,
+ color: Theme.of(context).textTheme.headline3!.color!,
),
)
];
@@ -210,7 +210,7 @@ class IoniaGiftCardDetailPage extends BasePage {
.expand((e) => e)
.toList()),
actionTitle: S.of(context).send_got_it,
- );
+ );
});
}
}
diff --git a/lib/src/screens/ionia/cards/ionia_more_options_page.dart b/lib/src/screens/ionia/cards/ionia_more_options_page.dart
index 84f0bed30..7d38c5e1e 100644
--- a/lib/src/screens/ionia/cards/ionia_more_options_page.dart
+++ b/lib/src/screens/ionia/cards/ionia_more_options_page.dart
@@ -5,7 +5,6 @@ import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/typography.dart';
import 'package:flutter/material.dart';
-
class IoniaMoreOptionsPage extends BasePage {
IoniaMoreOptionsPage(this.giftCard);
@@ -16,7 +15,7 @@ class IoniaMoreOptionsPage extends BasePage {
return Text(
S.current.more_options,
style: textMediumSemiBold(
- color: Theme.of(context).accentTextTheme!.headline1!.backgroundColor!,
+ color: Theme.of(context).accentTextTheme.headline1!.backgroundColor!,
),
);
}
@@ -27,40 +26,45 @@ class IoniaMoreOptionsPage extends BasePage {
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
- children: [
- SizedBox(height: 10,),
- Center(child: Text(S.of(context).choose_from_available_options, style: textMedium(
- color: Theme.of(context).primaryTextTheme!.headline6!.color!,
- ),)),
- SizedBox(height: 40,),
- InkWell(
- onTap: () async {
- final amount = await Navigator.of(context).pushNamed(Routes.ioniaCustomRedeemPage, arguments: [giftCard]) as String;
- if(amount.isNotEmpty){
- Navigator.pop(context, amount);
- }
- },
- child: _GradiantContainer(
- content: Padding(
- padding: const EdgeInsets.only(top: 24, left: 20, right: 24, bottom: 50),
- child: Text(
- S.of(context).custom_redeem_amount,
- style: textXLargeSemiBold(),
- ),
- ),
- ),
- )
- ],
- ),
+ children: [
+ SizedBox(
+ height: 10,
+ ),
+ Center(
+ child: Text(
+ S.of(context).choose_from_available_options,
+ style: textMedium(
+ color: Theme.of(context).primaryTextTheme.headline6!.color!,
+ ),
+ ),
+ ),
+ SizedBox(height: 40),
+ InkWell(
+ onTap: () async {
+ final amount = await Navigator.of(context)
+ .pushNamed(Routes.ioniaCustomRedeemPage, arguments: [giftCard]) as String?;
+ if (amount != null && amount.isNotEmpty) {
+ Navigator.pop(context);
+ }
+ },
+ child: _GradiantContainer(
+ content: Padding(
+ padding: const EdgeInsets.only(top: 24, left: 20, right: 24, bottom: 50),
+ child: Text(
+ S.of(context).custom_redeem_amount,
+ style: textXLargeSemiBold(),
+ ),
+ ),
+ ),
+ )
+ ],
+ ),
);
}
}
class _GradiantContainer extends StatelessWidget {
- const _GradiantContainer({
- Key? key,
- required this.content
- }) : super(key: key);
+ const _GradiantContainer({Key? key, required this.content}) : super(key: key);
final Widget content;
diff --git a/lib/src/screens/root/root.dart b/lib/src/screens/root/root.dart
index c507f6e1f..2526d15f3 100644
--- a/lib/src/screens/root/root.dart
+++ b/lib/src/screens/root/root.dart
@@ -1,23 +1,28 @@
import 'dart:async';
+import 'package:cake_wallet/core/auth_service.dart';
+import 'package:cake_wallet/utils/payment_request.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/auth/auth_page.dart';
import 'package:cake_wallet/store/app_store.dart';
import 'package:cake_wallet/store/authentication_store.dart';
import 'package:cake_wallet/entities/qr_scanner.dart';
+import 'package:uni_links/uni_links.dart';
class Root extends StatefulWidget {
- Root(
- {required Key key,
- required this.authenticationStore,
- required this.appStore,
- required this.child,
- required this.navigatorKey})
- : super(key: key);
+ Root({
+ required Key key,
+ required this.authenticationStore,
+ required this.appStore,
+ required this.child,
+ required this.navigatorKey,
+ required this.authService,
+ }) : super(key: key);
final AuthenticationStore authenticationStore;
final AppStore appStore;
final GlobalKey navigatorKey;
+ final AuthService authService;
final Widget child;
@override
@@ -26,22 +31,56 @@ class Root extends StatefulWidget {
class RootState extends State with WidgetsBindingObserver {
RootState()
- : _isInactiveController = StreamController.broadcast(),
- _isInactive = false,
- _postFrameCallback = false;
+ : _isInactiveController = StreamController.broadcast(),
+ _isInactive = false,
+ _requestAuth = true,
+ _postFrameCallback = false;
Stream get isInactive => _isInactiveController.stream;
StreamController _isInactiveController;
bool _isInactive;
bool _postFrameCallback;
+ bool _requestAuth;
+
+ StreamSubscription? stream;
+ Uri? launchUri;
@override
void initState() {
+ _requestAuth = widget.authService.requireAuth();
_isInactiveController = StreamController.broadcast();
_isInactive = false;
_postFrameCallback = false;
WidgetsBinding.instance.addObserver(this);
super.initState();
+
+ initUniLinks();
+ }
+
+ @override
+ void dispose() {
+ stream?.cancel();
+ super.dispose();
+ }
+
+ /// handle app links while the app is already started
+ /// whether its in the foreground or in the background.
+ Future initUniLinks() async {
+ try {
+ stream = uriLinkStream.listen((Uri? uri) {
+ handleDeepLinking(uri);
+ });
+
+ handleDeepLinking(await getInitialUri());
+ } catch (e) {
+ print(e);
+ }
+ }
+
+ void handleDeepLinking(Uri? uri) {
+ if (uri == null || !mounted) return;
+
+ launchUri = uri;
}
@override
@@ -52,11 +91,15 @@ class RootState extends State with WidgetsBindingObserver {
return;
}
- if (!_isInactive &&
- widget.authenticationStore.state == AuthenticationState.allowed) {
+ if (!_isInactive && widget.authenticationStore.state == AuthenticationState.allowed) {
setState(() => _setInactive(true));
}
+ break;
+ case AppLifecycleState.resumed:
+ setState(() {
+ _requestAuth = widget.authService.requireAuth();
+ });
break;
default:
break;
@@ -65,7 +108,7 @@ class RootState extends State with WidgetsBindingObserver {
@override
Widget build(BuildContext context) {
- if (_isInactive && !_postFrameCallback) {
+ if (_isInactive && !_postFrameCallback && _requestAuth) {
_postFrameCallback = true;
WidgetsBinding.instance.addPostFrameCallback((_) {
widget.navigatorKey.currentState?.pushNamed(Routes.unlock,
@@ -75,9 +118,19 @@ class RootState extends State with WidgetsBindingObserver {
}
_reset();
- auth.close();
+ auth.close(
+ route: launchUri != null ? Routes.send : null,
+ arguments: PaymentRequest.fromUri(launchUri),
+ );
+ launchUri = null;
});
});
+ } else if (launchUri != null) {
+ widget.navigatorKey.currentState?.pushNamed(
+ Routes.send,
+ arguments: PaymentRequest.fromUri(launchUri),
+ );
+ launchUri = null;
}
return WillPopScope(onWillPop: () async => false, child: widget.child);
diff --git a/lib/src/screens/send/send_page.dart b/lib/src/screens/send/send_page.dart
index 190127c0c..2cd849f34 100644
--- a/lib/src/screens/send/send_page.dart
+++ b/lib/src/screens/send/send_page.dart
@@ -4,6 +4,7 @@ import 'package:cake_wallet/src/screens/send/widgets/send_card.dart';
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
import 'package:cake_wallet/src/widgets/picker.dart';
import 'package:cake_wallet/src/widgets/template_tile.dart';
+import 'package:cake_wallet/utils/payment_request.dart';
import 'package:cake_wallet/view_model/send/output.dart';
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
@@ -25,11 +26,15 @@ import 'package:smooth_page_indicator/smooth_page_indicator.dart';
import 'package:cw_core/crypto_currency.dart';
class SendPage extends BasePage {
- SendPage({required this.sendViewModel}) : _formKey = GlobalKey();
+ SendPage({
+ required this.sendViewModel,
+ this.initialPaymentRequest,
+ }) : _formKey = GlobalKey();
final SendViewModel sendViewModel;
final GlobalKey _formKey;
final controller = PageController(initialPage: 0);
+ final PaymentRequest? initialPaymentRequest;
bool _effectsInstalled = false;
@@ -116,6 +121,7 @@ class SendPage extends BasePage {
key: output.key,
output: output,
sendViewModel: sendViewModel,
+ initialPaymentRequest: initialPaymentRequest,
);
});
},
diff --git a/lib/src/screens/send/widgets/send_card.dart b/lib/src/screens/send/widgets/send_card.dart
index 8f0d6675d..082067d95 100644
--- a/lib/src/screens/send/widgets/send_card.dart
+++ b/lib/src/screens/send/widgets/send_card.dart
@@ -1,4 +1,5 @@
import 'package:cake_wallet/entities/priority_for_wallet_type.dart';
+import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cake_wallet/utils/payment_request.dart';
import 'package:cw_core/transaction_priority.dart';
import 'package:cake_wallet/routes.dart';
@@ -20,21 +21,28 @@ class SendCard extends StatefulWidget {
SendCard({
Key? key,
required this.output,
- required this.sendViewModel}) : super(key: key);
+ required this.sendViewModel,
+ this.initialPaymentRequest,
+ }) : super(key: key);
final Output output;
final SendViewModel sendViewModel;
+ final PaymentRequest? initialPaymentRequest;
@override
SendCardState createState() => SendCardState(
output: output,
- sendViewModel: sendViewModel
+ sendViewModel: sendViewModel,
+ initialPaymentRequest: initialPaymentRequest,
);
}
class SendCardState extends State
with AutomaticKeepAliveClientMixin {
- SendCardState({required this.output, required this.sendViewModel})
+ SendCardState({
+ required this.output,
+ required this.sendViewModel,
+ this.initialPaymentRequest})
: addressController = TextEditingController(),
cryptoAmountController = TextEditingController(),
fiatAmountController = TextEditingController(),
@@ -49,6 +57,7 @@ class SendCardState extends State
final Output output;
final SendViewModel sendViewModel;
+ final PaymentRequest? initialPaymentRequest;
final TextEditingController addressController;
final TextEditingController cryptoAmountController;
@@ -61,6 +70,27 @@ class SendCardState extends State
bool _effectsInstalled = false;
+ @override
+ void initState() {
+ super.initState();
+
+ /// if the current wallet doesn't match the one in the qr code
+ if (initialPaymentRequest != null &&
+ sendViewModel.walletCurrencyName != initialPaymentRequest!.scheme.toLowerCase()) {
+ WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
+ showPopUp(
+ context: context,
+ builder: (BuildContext context) {
+ return AlertWithOneAction(
+ alertTitle: S.of(context).error,
+ alertContent: S.of(context).unmatched_currencies,
+ buttonText: S.of(context).ok,
+ buttonAction: () => Navigator.of(context).pop());
+ });
+ });
+ }
+ }
+
@override
Widget build(BuildContext context) {
super.build(context);
@@ -512,8 +542,12 @@ class SendCardState extends State
}
void _setEffects(BuildContext context) {
- addressController.text = output.address;
- cryptoAmountController.text = output.cryptoAmount;
+ if (output.address.isNotEmpty) {
+ addressController.text = output.address;
+ }
+ if (output.cryptoAmount.isNotEmpty) {
+ cryptoAmountController.text = output.cryptoAmount;
+ }
fiatAmountController.text = output.fiatAmount;
noteController.text = output.note;
extractedAddressController.text = output.extractedAddress;
@@ -605,6 +639,13 @@ class SendCardState extends State
extractedAddressController.text = extractedAddress;
});
+ if (initialPaymentRequest != null &&
+ sendViewModel.walletCurrencyName == initialPaymentRequest!.scheme.toLowerCase()) {
+ addressController.text = initialPaymentRequest!.address;
+ cryptoAmountController.text = initialPaymentRequest!.amount;
+ noteController.text = initialPaymentRequest!.note;
+ }
+
_effectsInstalled = true;
}
diff --git a/lib/src/screens/settings/security_backup_page.dart b/lib/src/screens/settings/security_backup_page.dart
index 224c941ee..102a94b36 100644
--- a/lib/src/screens/settings/security_backup_page.dart
+++ b/lib/src/screens/settings/security_backup_page.dart
@@ -1,9 +1,11 @@
+import 'package:cake_wallet/entities/pin_code_required_duration.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/auth/auth_page.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/screens/pin_code/pin_code_widget.dart';
import 'package:cake_wallet/src/screens/settings/widgets/settings_cell_with_arrow.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/widgets/standard_list.dart';
import 'package:cake_wallet/view_model/settings/security_settings_view_model.dart';
@@ -25,22 +27,26 @@ class SecurityBackupPage extends BasePage {
child: Column(mainAxisSize: MainAxisSize.min, children: [
SettingsCellWithArrow(
title: S.current.show_keys,
- handler: (_) => Navigator.of(context).pushNamed(Routes.auth,
- arguments: (bool isAuthenticatedSuccessfully, AuthPageState auth) {
- if (isAuthenticatedSuccessfully) {
- auth.close(route: Routes.showKeys);
- }
- }),
+ handler: (_) => _securitySettingsViewModel.checkPinCodeRiquired()
+ ? Navigator.of(context).pushNamed(Routes.auth,
+ arguments: (bool isAuthenticatedSuccessfully, AuthPageState auth) {
+ if (isAuthenticatedSuccessfully) {
+ auth.close(route: Routes.showKeys);
+ }
+ })
+ : Navigator.of(context).pushNamed(Routes.showKeys),
),
StandardListSeparator(padding: EdgeInsets.symmetric(horizontal: 24)),
SettingsCellWithArrow(
title: S.current.create_backup,
- handler: (_) => Navigator.of(context).pushNamed(Routes.auth,
- arguments: (bool isAuthenticatedSuccessfully, AuthPageState auth) {
- if (isAuthenticatedSuccessfully) {
- auth.close(route: Routes.backup);
- }
- }),
+ handler: (_) => _securitySettingsViewModel.checkPinCodeRiquired()
+ ? Navigator.of(context).pushNamed(Routes.auth,
+ arguments: (bool isAuthenticatedSuccessfully, AuthPageState auth) {
+ if (isAuthenticatedSuccessfully) {
+ auth.close(route: Routes.backup);
+ }
+ })
+ : Navigator.of(context).pushNamed(Routes.backup),
),
StandardListSeparator(padding: EdgeInsets.symmetric(horizontal: 24)),
SettingsCellWithArrow(
@@ -65,10 +71,12 @@ class SecurityBackupPage extends BasePage {
arguments: (bool isAuthenticatedSuccessfully, AuthPageState auth) async {
if (isAuthenticatedSuccessfully) {
if (await _securitySettingsViewModel.biometricAuthenticated()) {
- _securitySettingsViewModel.setAllowBiometricalAuthentication(isAuthenticatedSuccessfully);
+ _securitySettingsViewModel
+ .setAllowBiometricalAuthentication(isAuthenticatedSuccessfully);
}
} else {
- _securitySettingsViewModel.setAllowBiometricalAuthentication(isAuthenticatedSuccessfully);
+ _securitySettingsViewModel
+ .setAllowBiometricalAuthentication(isAuthenticatedSuccessfully);
}
auth.close();
@@ -78,6 +86,16 @@ class SecurityBackupPage extends BasePage {
}
});
}),
+ Observer(builder: (_) {
+ return SettingsPickerCell(
+ title: S.current.require_pin_after,
+ items: PinCodeRequiredDuration.values,
+ selectedItem: _securitySettingsViewModel.pinCodeRequiredDuration,
+ onItemSelected: (PinCodeRequiredDuration code) {
+ _securitySettingsViewModel.setPinCodeRequiredDuration(code);
+ },
+ );
+ }),
]),
);
}
diff --git a/lib/src/screens/wallet_list/wallet_list_page.dart b/lib/src/screens/wallet_list/wallet_list_page.dart
index c1a7ea953..d76631b3f 100644
--- a/lib/src/screens/wallet_list/wallet_list_page.dart
+++ b/lib/src/screens/wallet_list/wallet_list_page.dart
@@ -220,68 +220,88 @@ class WalletListBodyState extends State {
}
Future _loadWallet(WalletListItem wallet) async {
- await Navigator.of(context).pushNamed(Routes.auth, arguments:
- (bool isAuthenticatedSuccessfully, AuthPageState auth) async {
- if (!isAuthenticatedSuccessfully) {
- return;
- }
+ if (await widget.walletListViewModel.checkIfAuthRequired()) {
+ await Navigator.of(context).pushNamed(Routes.auth,
+ arguments: (bool isAuthenticatedSuccessfully, AuthPageState auth) async {
+ if (!isAuthenticatedSuccessfully) {
+ return;
+ }
+ try {
+ auth.changeProcessText(S.of(context).wallet_list_loading_wallet(wallet.name));
+ await widget.walletListViewModel.loadWallet(wallet);
+ auth.hideProgressText();
+ auth.close();
+ WidgetsBinding.instance.addPostFrameCallback((_) {
+ Navigator.of(context).pop();
+ });
+ } catch (e) {
+ auth.changeProcessText(
+ S.of(context).wallet_list_failed_to_load(wallet.name, e.toString()));
+ }
+ });
+ } else {
try {
- auth.changeProcessText(
- S.of(context).wallet_list_loading_wallet(wallet.name));
+ changeProcessText(S.of(context).wallet_list_loading_wallet(wallet.name));
await widget.walletListViewModel.loadWallet(wallet);
- auth.hideProgressText();
- auth.close();
- WidgetsBinding.instance.addPostFrameCallback((_) {
- Navigator.of(context).pop();
- });
+ hideProgressText();
+ Navigator.of(context).pop();
} catch (e) {
- auth.changeProcessText(S
- .of(context)
- .wallet_list_failed_to_load(wallet.name, e.toString()));
+ changeProcessText(S.of(context).wallet_list_failed_to_load(wallet.name, e.toString()));
}
- });
+ }
}
Future _removeWallet(WalletListItem wallet) async {
- await Navigator.of(context).pushNamed(Routes.auth, arguments:
- (bool isAuthenticatedSuccessfully, AuthPageState auth) async {
- if (!isAuthenticatedSuccessfully) {
- return;
- }
-
- bool confirmed = false;
-
- await showPopUp(
- context: context,
- builder: (BuildContext context) {
- return AlertWithTwoActions(
- alertTitle: S.of(context).delete_wallet,
- alertContent: S.of(context).delete_wallet_confirm_message(wallet.name),
- leftButtonText: S.of(context).cancel,
- rightButtonText: S.of(context).delete,
- actionLeftButton: () => Navigator.of(context).pop(),
- actionRightButton: () {
- confirmed = true;
- Navigator.of(context).pop();
- },
- );
- });
-
- if (confirmed) {
- try {
- auth.changeProcessText(
- S.of(context).wallet_list_removing_wallet(wallet.name));
- await widget.walletListViewModel.remove(wallet);
- } catch (e) {
- auth.changeProcessText(S
- .of(context)
- .wallet_list_failed_to_remove(wallet.name, e.toString()));
+ if (widget.walletListViewModel.checkIfAuthRequired()) {
+ await Navigator.of(context).pushNamed(Routes.auth,
+ arguments: (bool isAuthenticatedSuccessfully, AuthPageState auth) async {
+ if (!isAuthenticatedSuccessfully) {
+ return;
}
- }
+ _onSuccessfulAuth(wallet, auth);
+ });
+ } else {
+ _onSuccessfulAuth(wallet, null);
+ }
+ }
- auth.close();
- });
+ void _onSuccessfulAuth(WalletListItem wallet, AuthPageState? auth) async {
+ bool confirmed = false;
+ await showPopUp(
+ context: context,
+ builder: (BuildContext context) {
+ return AlertWithTwoActions(
+ alertTitle: S.of(context).delete_wallet,
+ alertContent: S.of(context).delete_wallet_confirm_message(wallet.name),
+ leftButtonText: S.of(context).cancel,
+ rightButtonText: S.of(context).delete,
+ actionLeftButton: () => Navigator.of(context).pop(),
+ actionRightButton: () {
+ confirmed = true;
+ Navigator.of(context).pop();
+ },
+ );
+ });
+
+ if (confirmed) {
+ try {
+ auth != null
+ ? auth.changeProcessText(S.of(context).wallet_list_removing_wallet(wallet.name))
+ : changeProcessText(S.of(context).wallet_list_removing_wallet(wallet.name));
+ await widget.walletListViewModel.remove(wallet);
+ } catch (e) {
+ auth != null
+ ? auth.changeProcessText(
+ S.of(context).wallet_list_failed_to_remove(wallet.name, e.toString()),
+ )
+ : changeProcessText(
+ S.of(context).wallet_list_failed_to_remove(wallet.name, e.toString()),
+ );
+ }
+ }
+
+ auth?.close();
}
void changeProcessText(String text) {
@@ -294,16 +314,16 @@ class WalletListBodyState extends State {
}
ActionPane _actionPane(WalletListItem wallet) => ActionPane(
- motion: const ScrollMotion(),
- extentRatio: 0.3,
- children: [
- SlidableAction(
- onPressed: (_) => _removeWallet(wallet),
- backgroundColor: Colors.red,
- foregroundColor: Colors.white,
- icon: CupertinoIcons.delete,
- label: S.of(context).delete,
- ),
- ],
- );
+ motion: const ScrollMotion(),
+ extentRatio: 0.3,
+ children: [
+ SlidableAction(
+ onPressed: (_) => _removeWallet(wallet),
+ backgroundColor: Colors.red,
+ foregroundColor: Colors.white,
+ icon: CupertinoIcons.delete,
+ label: S.of(context).delete,
+ ),
+ ],
+ );
}
diff --git a/lib/store/dashboard/trades_store.dart b/lib/store/dashboard/trades_store.dart
index 6e763196b..72442b46f 100644
--- a/lib/store/dashboard/trades_store.dart
+++ b/lib/store/dashboard/trades_store.dart
@@ -31,13 +31,8 @@ abstract class TradesStoreBase with Store {
void setTrade(Trade trade) => this.trade = trade;
@action
- Future updateTradeList() async {
- if (trade == null) {
- return;
- }
-
- trades = tradesSource.values.map((trade) => TradeListItem(
- trade: trade!,
- settingsStore: settingsStore)).toList();
- }
+ Future updateTradeList() async => trades =
+ tradesSource.values.map((trade) => TradeListItem(
+ trade: trade,
+ settingsStore: settingsStore)).toList();
}
\ No newline at end of file
diff --git a/lib/store/settings_store.dart b/lib/store/settings_store.dart
index 96d74a23a..b10e0d08d 100644
--- a/lib/store/settings_store.dart
+++ b/lib/store/settings_store.dart
@@ -1,4 +1,5 @@
import 'package:cake_wallet/bitcoin/bitcoin.dart';
+import 'package:cake_wallet/entities/pin_code_required_duration.dart';
import 'package:cake_wallet/entities/preferences_key.dart';
import 'package:cw_core/transaction_priority.dart';
import 'package:cake_wallet/themes/theme_base.dart';
@@ -17,7 +18,6 @@ import 'package:cw_core/node.dart';
import 'package:cake_wallet/monero/monero.dart';
import 'package:cake_wallet/entities/action_list_display_mode.dart';
import 'package:cake_wallet/entities/fiat_api_mode.dart';
-import 'package:cake_wallet/.secrets.g.dart' as secrets;
part 'settings_store.g.dart';
@@ -41,6 +41,7 @@ abstract class SettingsStoreBase with Store {
required this.shouldShowYatPopup,
required this.isBitcoinBuyEnabled,
required this.actionlistDisplayMode,
+ required this.pinTimeOutDuration,
TransactionPriority? initialBitcoinTransactionPriority,
TransactionPriority? initialMoneroTransactionPriority,
TransactionPriority? initialHavenTransactionPriority,
@@ -141,6 +142,11 @@ abstract class SettingsStoreBase with Store {
(String languageCode) => sharedPreferences.setString(
PreferencesKey.currentLanguageCode, languageCode));
+ reaction(
+ (_) => pinTimeOutDuration,
+ (PinCodeRequiredDuration pinCodeInterval) => sharedPreferences.setInt(
+ PreferencesKey.pinTimeOutDuration, pinCodeInterval.value));
+
reaction(
(_) => balanceDisplayMode,
(BalanceDisplayMode mode) => sharedPreferences.setInt(
@@ -162,6 +168,7 @@ abstract class SettingsStoreBase with Store {
static const defaultPinLength = 4;
static const defaultActionsMode = 11;
+ static const defaultPinCodeTimeOutDuration = PinCodeRequiredDuration.tenminutes;
@observable
FiatCurrency fiatCurrency;
@@ -193,6 +200,9 @@ abstract class SettingsStoreBase with Store {
@observable
int pinCodeLength;
+ @observable
+ PinCodeRequiredDuration pinTimeOutDuration;
+
@computed
ThemeData get theme => currentTheme.themeData;
@@ -288,6 +298,11 @@ abstract class SettingsStoreBase with Store {
sharedPreferences.getInt(PreferencesKey.displayActionListModeKey) ??
defaultActionsMode));
var pinLength = sharedPreferences.getInt(PreferencesKey.currentPinLength);
+ final timeOutDuration = sharedPreferences.getInt(PreferencesKey.pinTimeOutDuration);
+ final pinCodeTimeOutDuration = timeOutDuration != null
+ ? PinCodeRequiredDuration.deserialize(raw: timeOutDuration)
+ : defaultPinCodeTimeOutDuration;
+
// If no value
if (pinLength == null || pinLength == 0) {
pinLength = defaultPinLength;
@@ -343,6 +358,7 @@ abstract class SettingsStoreBase with Store {
initialTheme: savedTheme,
actionlistDisplayMode: actionListDisplayMode,
initialPinLength: pinLength,
+ pinTimeOutDuration: pinCodeTimeOutDuration,
initialLanguageCode: savedLanguageCode,
initialMoneroTransactionPriority: moneroTransactionPriority,
initialBitcoinTransactionPriority: bitcoinTransactionPriority,
diff --git a/lib/utils/payment_request.dart b/lib/utils/payment_request.dart
index 26244059f..88730f037 100644
--- a/lib/utils/payment_request.dart
+++ b/lib/utils/payment_request.dart
@@ -1,22 +1,25 @@
class PaymentRequest {
- PaymentRequest(this.address, this.amount, this.note);
+ PaymentRequest(this.address, this.amount, this.note, this.scheme);
- factory PaymentRequest.fromUri(Uri uri) {
+ factory PaymentRequest.fromUri(Uri? uri) {
var address = "";
var amount = "";
var note = "";
+ var scheme = "";
if (uri != null) {
address = uri.path;
amount = uri.queryParameters['tx_amount'] ?? uri.queryParameters['amount'] ?? "";
note = uri.queryParameters['tx_description']
?? uri.queryParameters['message'] ?? "";
+ scheme = uri.scheme;
}
- return PaymentRequest(address, amount, note);
+ return PaymentRequest(address, amount, note, scheme);
}
final String address;
final String amount;
final String note;
+ final String scheme;
}
\ No newline at end of file
diff --git a/lib/view_model/auth_view_model.dart b/lib/view_model/auth_view_model.dart
index 29ef46b47..e50f4db0c 100644
--- a/lib/view_model/auth_view_model.dart
+++ b/lib/view_model/auth_view_model.dart
@@ -14,10 +14,12 @@ part 'auth_view_model.g.dart';
class AuthViewModel = AuthViewModelBase with _$AuthViewModel;
abstract class AuthViewModelBase with Store {
- AuthViewModelBase(this._authService, this._sharedPreferences,
- this._settingsStore, this._biometricAuth)
+ AuthViewModelBase(
+ this._authService, this._sharedPreferences, this._settingsStore, this._biometricAuth)
: _failureCounter = 0,
- state = InitialExecutionState();
+ state = InitialExecutionState() {
+ reaction((_) => state, _saveLastAuthTime);
+ }
static const maxFailedLogins = 3;
static const banTimeout = 180; // 3 minutes
@@ -28,8 +30,7 @@ abstract class AuthViewModelBase with Store {
int get pinLength => _settingsStore.pinCodeLength;
- bool get isBiometricalAuthenticationAllowed =>
- _settingsStore.allowBiometricalAuthentication;
+ bool get isBiometricalAuthenticationAllowed => _settingsStore.allowBiometricalAuthentication;
@observable
int _failureCounter;
@@ -114,8 +115,14 @@ abstract class AuthViewModelBase with Store {
state = ExecutedSuccessfullyState();
}
}
- } catch(e) {
+ } catch (e) {
state = FailureState(e.toString());
}
}
+
+ void _saveLastAuthTime(ExecutionState state) {
+ if (state is ExecutedSuccessfullyState) {
+ _authService.saveLastAuthTime();
+ }
+ }
}
diff --git a/lib/view_model/ionia/ionia_custom_redeem_view_model.dart b/lib/view_model/ionia/ionia_custom_redeem_view_model.dart
index 963604c15..5776443ee 100644
--- a/lib/view_model/ionia/ionia_custom_redeem_view_model.dart
+++ b/lib/view_model/ionia/ionia_custom_redeem_view_model.dart
@@ -1,29 +1,51 @@
+import 'package:cake_wallet/core/execution_state.dart';
import 'package:cake_wallet/ionia/ionia_gift_card.dart';
+import 'package:cake_wallet/ionia/ionia_service.dart';
import 'package:mobx/mobx.dart';
part 'ionia_custom_redeem_view_model.g.dart';
+
class IoniaCustomRedeemViewModel = IoniaCustomRedeemViewModelBase with _$IoniaCustomRedeemViewModel;
abstract class IoniaCustomRedeemViewModelBase with Store {
- IoniaCustomRedeemViewModelBase(this.giftCard)
- : amount = 0;
+ IoniaCustomRedeemViewModelBase({
+ required this.giftCard,
+ required this.ioniaService,
+ }) : amount = 0,
+ redeemState = InitialExecutionState();
final IoniaGiftCard giftCard;
+ final IoniaService ioniaService;
+
+ @observable
+ ExecutionState redeemState;
+
@observable
double amount;
@computed
- double get remaining => amount <= giftCard.remainingAmount ? giftCard.remainingAmount - amount : 0;
+ double get remaining =>
+ amount <= giftCard.remainingAmount ? giftCard.remainingAmount - amount : 0;
@computed
- String get formattedRemaining => remaining.toStringAsFixed(2);
+ String get formattedRemaining => remaining.toStringAsFixed(2);
@computed
bool get disableRedeem => amount > giftCard.remainingAmount;
@action
- void updateAmount(String text){
- amount = text.isEmpty ? 0 : (double.parse(text.replaceAll(',', '.')) ?? 0);
+ void updateAmount(String text) {
+ amount = double.tryParse(text.replaceAll(',', '.')) ?? 0;
}
-}
\ No newline at end of file
+ @action
+ Future addCustomRedeem() async {
+ try {
+ redeemState = IsExecutingState();
+ await ioniaService.redeem(giftCardId: giftCard.id, amount: amount);
+ redeemState = ExecutedSuccessfullyState();
+ } catch (e) {
+ redeemState = FailureState(e.toString());
+ }
+ }
+}
diff --git a/lib/view_model/ionia/ionia_gift_card_details_view_model.dart b/lib/view_model/ionia/ionia_gift_card_details_view_model.dart
index 8e0a2795b..cbf5ebc78 100644
--- a/lib/view_model/ionia/ionia_gift_card_details_view_model.dart
+++ b/lib/view_model/ionia/ionia_gift_card_details_view_model.dart
@@ -6,21 +6,19 @@ import 'package:device_display_brightness/device_display_brightness.dart';
part 'ionia_gift_card_details_view_model.g.dart';
-class IoniaGiftCardDetailsViewModel = IoniaGiftCardDetailsViewModelBase with _$IoniaGiftCardDetailsViewModel;
+class IoniaGiftCardDetailsViewModel = IoniaGiftCardDetailsViewModelBase
+ with _$IoniaGiftCardDetailsViewModel;
abstract class IoniaGiftCardDetailsViewModelBase with Store {
-
- IoniaGiftCardDetailsViewModelBase({
- required this.ioniaService,
- required this.giftCard})
- : redeemState = InitialExecutionState(),
- remainingAmount = giftCard.remainingAmount,
- brightness = 0;
+ IoniaGiftCardDetailsViewModelBase({required this.ioniaService, required this.giftCard})
+ : redeemState = InitialExecutionState(),
+ remainingAmount = giftCard.remainingAmount,
+ brightness = 0;
final IoniaService ioniaService;
-
+
double brightness;
-
+
@observable
IoniaGiftCard giftCard;
@@ -35,21 +33,22 @@ abstract class IoniaGiftCardDetailsViewModelBase with Store {
giftCard.remainingAmount = remainingAmount;
try {
redeemState = IsExecutingState();
- await ioniaService.redeem(giftCard);
+ await ioniaService.redeem(giftCardId: giftCard.id, amount: giftCard.remainingAmount);
giftCard = await ioniaService.getGiftCard(id: giftCard.id);
redeemState = ExecutedSuccessfullyState();
- } catch(e) {
+ } catch (e) {
redeemState = FailureState(e.toString());
}
}
@action
- void updateRemaining(double amount){
- remainingAmount = amount;
+ Future refeshCard() async {
+ giftCard = await ioniaService.getGiftCard(id: giftCard.id);
+ remainingAmount = giftCard.remainingAmount;
}
void increaseBrightness() async {
brightness = await DeviceDisplayBrightness.getBrightness();
await DeviceDisplayBrightness.setBrightness(1.0);
}
-}
\ No newline at end of file
+}
diff --git a/lib/view_model/send/send_view_model.dart b/lib/view_model/send/send_view_model.dart
index fd783cf6f..b6604789a 100644
--- a/lib/view_model/send/send_view_model.dart
+++ b/lib/view_model/send/send_view_model.dart
@@ -53,7 +53,7 @@ abstract class SendViewModelBase with Store {
outputs.add(Output(_wallet, _settingsStore, _fiatConversationStore, () => selectedCryptoCurrency));
}
-
+
@observable
ExecutionState state;
@@ -180,6 +180,8 @@ abstract class SendViewModelBase with Store {
WalletType get walletType => _wallet.type;
+ String? get walletCurrencyName => _wallet.currency.name?.toLowerCase();
+
bool get hasCurrecyChanger => walletType == WalletType.haven;
@computed
@@ -306,7 +308,7 @@ abstract class SendViewModelBase with Store {
@action
void onClose() =>
_settingsStore.fiatCurrency = fiatFromSettings;
-
+
@action
void setFiatCurrency(FiatCurrency fiat) =>
_settingsStore.fiatCurrency = fiat;
diff --git a/lib/view_model/settings/security_settings_view_model.dart b/lib/view_model/settings/security_settings_view_model.dart
index 4a88b633f..c48223af6 100644
--- a/lib/view_model/settings/security_settings_view_model.dart
+++ b/lib/view_model/settings/security_settings_view_model.dart
@@ -1,4 +1,6 @@
+import 'package:cake_wallet/core/auth_service.dart';
import 'package:cake_wallet/entities/biometric_auth.dart';
+import 'package:cake_wallet/entities/pin_code_required_duration.dart';
import 'package:cake_wallet/store/settings_store.dart';
import 'package:mobx/mobx.dart';
@@ -7,19 +9,33 @@ part 'security_settings_view_model.g.dart';
class SecuritySettingsViewModel = SecuritySettingsViewModelBase with _$SecuritySettingsViewModel;
abstract class SecuritySettingsViewModelBase with Store {
- SecuritySettingsViewModelBase(this._settingsStore) : _biometricAuth = BiometricAuth();
+ SecuritySettingsViewModelBase(
+ this._settingsStore,
+ this._authService,
+ ) : _biometricAuth = BiometricAuth();
final BiometricAuth _biometricAuth;
final SettingsStore _settingsStore;
+ final AuthService _authService;
@computed
bool get allowBiometricalAuthentication => _settingsStore.allowBiometricalAuthentication;
+ @computed
+ PinCodeRequiredDuration get pinCodeRequiredDuration => _settingsStore.pinTimeOutDuration;
+
@action
Future biometricAuthenticated() async {
return await _biometricAuth.canCheckBiometrics() && await _biometricAuth.isAuthenticated();
}
@action
- void setAllowBiometricalAuthentication(bool value) => _settingsStore.allowBiometricalAuthentication = value;
+ void setAllowBiometricalAuthentication(bool value) =>
+ _settingsStore.allowBiometricalAuthentication = value;
+
+ @action
+ setPinCodeRequiredDuration(PinCodeRequiredDuration duration) =>
+ _settingsStore.pinTimeOutDuration = duration;
+
+ bool checkPinCodeRiquired() => _authService.requireAuth();
}
diff --git a/lib/view_model/wallet_list/wallet_list_view_model.dart b/lib/view_model/wallet_list/wallet_list_view_model.dart
index 0bbc68748..6d63675ba 100644
--- a/lib/view_model/wallet_list/wallet_list_view_model.dart
+++ b/lib/view_model/wallet_list/wallet_list_view_model.dart
@@ -1,5 +1,5 @@
+import 'package:cake_wallet/core/auth_service.dart';
import 'package:cake_wallet/core/wallet_loading_service.dart';
-import 'package:cake_wallet/view_model/wallet_new_vm.dart';
import 'package:hive/hive.dart';
import 'package:mobx/mobx.dart';
import 'package:cake_wallet/di.dart';
@@ -15,9 +15,12 @@ part 'wallet_list_view_model.g.dart';
class WalletListViewModel = WalletListViewModelBase with _$WalletListViewModel;
abstract class WalletListViewModelBase with Store {
- WalletListViewModelBase(this._walletInfoSource, this._appStore,
- this._walletLoadingService)
- : wallets = ObservableList() {
+ WalletListViewModelBase(
+ this._walletInfoSource,
+ this._appStore,
+ this._walletLoadingService,
+ this._authService,
+ ) : wallets = ObservableList() {
_updateList();
}
@@ -27,6 +30,7 @@ abstract class WalletListViewModelBase with Store {
final AppStore _appStore;
final Box _walletInfoSource;
final WalletLoadingService _walletLoadingService;
+ final AuthService _authService;
WalletType get currentWalletType => _appStore.wallet!.type;
@@ -47,12 +51,20 @@ abstract class WalletListViewModelBase with Store {
void _updateList() {
wallets.clear();
- wallets.addAll(_walletInfoSource.values.map((info) => WalletListItem(
- name: info.name,
- type: info.type,
- key: info.key,
- isCurrent: info.name == _appStore.wallet!.name &&
- info.type == _appStore.wallet!.type,
- isEnabled: availableWalletTypes.contains(info.type))));
+ wallets.addAll(
+ _walletInfoSource.values.map(
+ (info) => WalletListItem(
+ name: info.name,
+ type: info.type,
+ key: info.key,
+ isCurrent: info.name == _appStore.wallet!.name && info.type == _appStore.wallet!.type,
+ isEnabled: availableWalletTypes.contains(info.type),
+ ),
+ ),
+ );
+ }
+
+ bool checkIfAuthRequired() {
+ return _authService.requireAuth();
}
}
diff --git a/res/values/strings_de.arb b/res/values/strings_de.arb
index 404e92282..bb01cdaba 100644
--- a/res/values/strings_de.arb
+++ b/res/values/strings_de.arb
@@ -663,6 +663,9 @@
"privacy": "Datenschutz",
"display_settings": "Anzeigeeinstellungen",
"other_settings": "Andere Einstellungen",
+ "require_pin_after": "PIN anfordern nach",
+ "always": "immer",
+ "minutes_to_pin_code": "${minute} Minuten",
"disable_exchange": "Exchange deaktivieren",
"advanced_privacy_settings": "Erweiterte Datenschutzeinstellungen",
"settings_can_be_changed_later": "Diese Einstellungen können später in den App-Einstellungen geändert werden",
@@ -671,5 +674,6 @@
"fiat_api": "Fiat API",
"disabled": "Deaktiviert",
"enabled": "Ermöglicht",
- "tor_only": "Nur Tor"
+ "tor_only": "Nur Tor",
+ "unmatched_currencies": "Die Währung Ihres aktuellen Wallets stimmt nicht mit der des gescannten QR überein"
}
diff --git a/res/values/strings_en.arb b/res/values/strings_en.arb
index e405dd57b..8cd6a3942 100644
--- a/res/values/strings_en.arb
+++ b/res/values/strings_en.arb
@@ -663,6 +663,9 @@
"privacy": "Privacy",
"display_settings": "Display settings",
"other_settings": "Other settings",
+ "require_pin_after": "Require PIN after",
+ "always": "Always",
+ "minutes_to_pin_code": "${minute} minutes",
"disable_exchange": "Disable exchange",
"advanced_privacy_settings": "Advanced Privacy Settings",
"settings_can_be_changed_later": "These settings can be changed later in the app settings",
@@ -671,5 +674,6 @@
"fiat_api": "Fiat API",
"disabled": "Disabled",
"enabled": "Enabled",
- "tor_only": "Tor only"
+ "tor_only": "Tor only",
+ "unmatched_currencies": "Your current wallet's currency does not match that of the scanned QR"
}
diff --git a/res/values/strings_es.arb b/res/values/strings_es.arb
index b9691d8c1..63fca7fbf 100644
--- a/res/values/strings_es.arb
+++ b/res/values/strings_es.arb
@@ -663,6 +663,9 @@
"privacy": "Privacidad",
"display_settings": "Configuración de pantalla",
"other_settings": "Otras configuraciones",
+ "require_pin_after": "Requerir PIN después de",
+ "always": "siempre",
+ "minutes_to_pin_code": "${minute} minutos",
"disable_exchange": "Deshabilitar intercambio",
"advanced_privacy_settings": "Configuración avanzada de privacidad",
"settings_can_be_changed_later": "Estas configuraciones se pueden cambiar más tarde en la configuración de la aplicación",
@@ -671,5 +674,6 @@
"fiat_api": "Fiat API",
"disabled": "Desactivado",
"enabled": "Activado",
- "tor_only": "solo Tor"
+ "tor_only": "solo Tor",
+ "unmatched_currencies": "La moneda de su billetera actual no coincide con la del QR escaneado"
}
diff --git a/res/values/strings_fr.arb b/res/values/strings_fr.arb
index ac1c11a4a..dc975ac24 100644
--- a/res/values/strings_fr.arb
+++ b/res/values/strings_fr.arb
@@ -661,6 +661,9 @@
"privacy": "Confidentialité",
"display_settings": "Paramètres d'affichage",
"other_settings": "Autres paramètres",
+ "require_pin_after": "NIP requis après",
+ "always": "toujours",
+ "minutes_to_pin_code": "${minute} minutes",
"disable_exchange": "Désactiver l'échange",
"advanced_privacy_settings": "Paramètres de confidentialité avancés",
"settings_can_be_changed_later": "Ces paramètres peuvent être modifiés ultérieurement dans les paramètres de l'application",
@@ -669,5 +672,6 @@
"fiat_api": "Fiat API",
"disabled": "Handicapé",
"enabled": "Activé",
- "tor_only": "Tor uniquement"
+ "tor_only": "Tor uniquement",
+ "unmatched_currencies": "La devise de votre portefeuille actuel ne correspond pas à celle du QR scanné"
}
diff --git a/res/values/strings_hi.arb b/res/values/strings_hi.arb
index 3d984decb..7cb5ff132 100644
--- a/res/values/strings_hi.arb
+++ b/res/values/strings_hi.arb
@@ -663,12 +663,17 @@
"privacy": "गोपनीयता",
"display_settings": "प्रदर्शन सेटिंग्स",
"other_settings": "अन्य सेटिंग्स",
+ "require_pin_after": "इसके बाद पिन आवश्यक है",
+ "always": "हमेशा",
+ "minutes_to_pin_code": "${minute} मिनट",
+ "disable_exchange": "एक्सचेंज अक्षम करें",
"advanced_privacy_settings": "उन्नत गोपनीयता सेटिंग्स",
"settings_can_be_changed_later": "इन सेटिंग्स को बाद में ऐप सेटिंग में बदला जा सकता है",
"add_custom_node": "नया कस्टम नोड जोड़ें",
- "disable_exchange": "एक्सचेंज अक्षम करें",
+ "disable_fiat": "िएट को अक्षम करें",
"fiat_api": "फिएट पैसे API",
"disabled": "अक्षम",
"enabled": "सक्रिय",
- "tor_only": "Tor केवल"
+ "tor_only": "Tor केवल",
+ "unmatched_currencies": "आपके वर्तमान वॉलेट की मुद्रा स्कैन किए गए क्यूआर से मेल नहीं खाती"
}
diff --git a/res/values/strings_hr.arb b/res/values/strings_hr.arb
index 048c9fb85..b2696a3df 100644
--- a/res/values/strings_hr.arb
+++ b/res/values/strings_hr.arb
@@ -663,6 +663,9 @@
"privacy": "Privatnost",
"display_settings": "Postavke zaslona",
"other_settings": "Ostale postavke",
+ "require_pin_after": "Zahtijevaj PIN nakon",
+ "always": "Uvijek",
+ "minutes_to_pin_code": "${minute} minuta",
"disable_exchange": "Onemogući exchange",
"advanced_privacy_settings": "Napredne postavke privatnosti",
"settings_can_be_changed_later": "Te se postavke mogu promijeniti kasnije u postavkama aplikacije",
@@ -671,5 +674,6 @@
"fiat_api": "Fiat API",
"disabled": "Onemogućeno",
"enabled": "Omogućeno",
- "tor_only": "Samo Tor"
+ "tor_only": "Samo Tor",
+ "unmatched_currencies": "Valuta vašeg trenutnog novčanika ne odgovara onoj na skeniranom QR-u"
}
diff --git a/res/values/strings_it.arb b/res/values/strings_it.arb
index 0d8aeca13..cb68d9ea6 100644
--- a/res/values/strings_it.arb
+++ b/res/values/strings_it.arb
@@ -663,6 +663,9 @@
"privacy": "Privacy",
"display_settings": "Impostazioni di visualizzazione",
"other_settings": "Altre impostazioni",
+ "require_pin_after": "Richiedi PIN dopo",
+ "always": "sempre",
+ "minutes_to_pin_code": "${minute} minuti",
"disable_exchange": "Disabilita scambio",
"advanced_privacy_settings": "Impostazioni avanzate sulla privacy",
"settings_can_be_changed_later": "Queste impostazioni possono essere modificate in seguito nelle impostazioni dell'app",
@@ -671,5 +674,6 @@
"fiat_api": "Fiat API",
"disabled": "Disabilitato",
"enabled": "Abilitato",
- "tor_only": "Solo Tor"
+ "tor_only": "Solo Tor",
+ "unmatched_currencies": "La valuta del tuo portafoglio attuale non corrisponde a quella del QR scansionato"
}
diff --git a/res/values/strings_ja.arb b/res/values/strings_ja.arb
index ad16cd80b..f0d595c6e 100644
--- a/res/values/strings_ja.arb
+++ b/res/values/strings_ja.arb
@@ -663,6 +663,9 @@
"privacy": "プライバシー",
"display_settings": "表示設定",
"other_settings": "その他の設定",
+ "require_pin_after": "後に PIN が必要",
+ "always": "いつも",
+ "minutes_to_pin_code": "${minute} 分",
"disable_exchange": "交換を無効にする",
"advanced_privacy_settings": "高度なプライバシー設定",
"settings_can_be_changed_later": "これらの設定は、後でアプリの設定で変更できます",
@@ -671,5 +674,6 @@
"fiat_api": "不換紙幣 API",
"disabled": "無効",
"enabled": "有効",
- "tor_only": "Torのみ"
+ "tor_only": "Torのみ",
+ "unmatched_currencies": "現在のウォレットの通貨がスキャンされたQRの通貨と一致しません"
}
diff --git a/res/values/strings_ko.arb b/res/values/strings_ko.arb
index c0daae349..f4d289287 100644
--- a/res/values/strings_ko.arb
+++ b/res/values/strings_ko.arb
@@ -663,6 +663,9 @@
"privacy": "프라이버시",
"display_settings": "디스플레이 설정",
"other_settings": "기타 설정",
+ "require_pin_after": "다음 이후에 PIN 필요",
+ "always": "언제나",
+ "minutes_to_pin_code": "${minute}분",
"disable_exchange": "교환 비활성화",
"advanced_privacy_settings": "고급 개인 정보 설정",
"settings_can_be_changed_later": "이 설정은 나중에 앱 설정에서 변경할 수 있습니다.",
@@ -671,5 +674,6 @@
"fiat_api": "명목 화폐 API",
"disabled": "장애가 있는",
"enabled": "사용",
- "tor_only": "Tor 뿐"
+ "tor_only": "Tor 뿐",
+ "unmatched_currencies": "현재 지갑의 통화가 스캔한 QR의 통화와 일치하지 않습니다."
}
diff --git a/res/values/strings_nl.arb b/res/values/strings_nl.arb
index 6db822576..06c075092 100644
--- a/res/values/strings_nl.arb
+++ b/res/values/strings_nl.arb
@@ -663,6 +663,9 @@
"privacy": "Privacy",
"display_settings": "Weergave-instellingen",
"other_settings": "Andere instellingen",
+ "require_pin_after": "Pincode vereist na",
+ "always": "altijd",
+ "minutes_to_pin_code": "${minute} minuten",
"disable_exchange": "Uitwisseling uitschakelen",
"advanced_privacy_settings": "Geavanceerde privacy-instellingen",
"settings_can_be_changed_later": "Deze instellingen kunnen later worden gewijzigd in de app-instellingen",
@@ -671,5 +674,6 @@
"fiat_api": "Fiat API",
"disabled": "Gehandicapt",
"enabled": "Ingeschakeld",
- "tor_only": "Alleen Tor"
+ "tor_only": "Alleen Tor",
+ "unmatched_currencies": "De valuta van uw huidige portemonnee komt niet overeen met die van de gescande QR"
}
diff --git a/res/values/strings_pl.arb b/res/values/strings_pl.arb
index bb27cb0d9..64e5f508b 100644
--- a/res/values/strings_pl.arb
+++ b/res/values/strings_pl.arb
@@ -663,6 +663,9 @@
"privacy": "Prywatność",
"display_settings": "Ustawienia wyświetlania",
"other_settings": "Inne ustawienia",
+ "require_pin_after": "Wymagaj kodu PIN po",
+ "always": "zawsze",
+ "minutes_to_pin_code": "${minute} minut",
"disable_exchange": "Wyłącz wymianę",
"advanced_privacy_settings": "Zaawansowane ustawienia prywatności",
"settings_can_be_changed_later": "Te ustawienia można później zmienić w ustawieniach aplikacji",
@@ -671,5 +674,6 @@
"fiat_api": "API Fiata",
"disabled": "Wyłączone",
"enabled": "Włączony",
- "tor_only": "Tylko Tor"
+ "tor_only": "Tylko Tor",
+ "unmatched_currencies": "Waluta Twojego obecnego portfela nie odpowiada walucie zeskanowanego kodu QR"
}
diff --git a/res/values/strings_pt.arb b/res/values/strings_pt.arb
index cb2f3b694..b23aa20e8 100644
--- a/res/values/strings_pt.arb
+++ b/res/values/strings_pt.arb
@@ -662,6 +662,9 @@
"privacy": "Privacidade",
"display_settings": "Configurações de exibição",
"other_settings": "Outras configurações",
+ "require_pin_after": "Exigir PIN após",
+ "always": "sempre",
+ "minutes_to_pin_code": "${minute} minutos",
"disable_exchange": "Desativar troca",
"advanced_privacy_settings": "Configurações de privacidade avançadas",
"settings_can_be_changed_later": "Essas configurações podem ser alteradas posteriormente nas configurações do aplicativo",
@@ -670,5 +673,6 @@
"fiat_api": "API da Fiat",
"disabled": "Desabilitado",
"enabled": "Habilitado",
- "tor_only": "Tor apenas"
+ "tor_only": "Tor apenas",
+ "unmatched_currencies": "A moeda da sua carteira atual não corresponde à do QR digitalizado"
}
diff --git a/res/values/strings_ru.arb b/res/values/strings_ru.arb
index 7496d6fbb..40f6ccdf0 100644
--- a/res/values/strings_ru.arb
+++ b/res/values/strings_ru.arb
@@ -369,7 +369,7 @@
"trade_for_not_created" : "Сделка для ${title} не создана.",
"trade_not_created" : "Сделка не создана",
"trade_id_not_found" : "Сделка ${tradeId} ${title} не найдена.",
- "trade_not_found" : "Trade not found.",
+ "trade_not_found" : "Торговля не найдена.",
"trade_state_pending" : "Ожидание",
@@ -663,6 +663,9 @@
"privacy": "Конфиденциальность",
"display_settings": "Настройки отображения",
"other_settings": "Другие настройки",
+ "require_pin_after": "Требовать ПИН после",
+ "always": "всегда",
+ "minutes_to_pin_code": "${minute} минут",
"disable_exchange": "Отключить обмен",
"advanced_privacy_settings": "Расширенные настройки конфиденциальности",
"settings_can_be_changed_later": "Эти настройки можно изменить позже в настройках приложения.",
@@ -671,5 +674,6 @@
"fiat_api": "Фиат API",
"disabled": "Отключено",
"enabled": "Включено",
- "tor_only": "Только Tor"
+ "tor_only": "Только Tor",
+ "unmatched_currencies": "Валюта вашего текущего кошелька не соответствует валюте отсканированного QR-кода."
}
diff --git a/res/values/strings_uk.arb b/res/values/strings_uk.arb
index e3b12a738..9108c700d 100644
--- a/res/values/strings_uk.arb
+++ b/res/values/strings_uk.arb
@@ -662,6 +662,9 @@
"privacy": "Конфіденційність",
"display_settings": "Налаштування дисплея",
"other_settings": "Інші налаштування",
+ "require_pin_after": "Вимагати PIN після",
+ "always": "Завжди",
+ "minutes_to_pin_code": "${minute} хвилин",
"disable_exchange": "Вимкнути exchange",
"advanced_privacy_settings": "Розширені налаштування конфіденційності",
"settings_can_be_changed_later": "Ці параметри можна змінити пізніше в налаштуваннях програми",
@@ -670,5 +673,6 @@
"fiat_api": "Фіат API",
"disabled": "Вимкнено",
"enabled": "Увімкнено",
- "tor_only": "Тільки Tor"
+ "tor_only": "Тільки Tor",
+ "unmatched_currencies": "Валюта вашого гаманця не збігається з валютою сканованого QR-коду"
}
diff --git a/res/values/strings_zh.arb b/res/values/strings_zh.arb
index e47d88186..4683bb068 100644
--- a/res/values/strings_zh.arb
+++ b/res/values/strings_zh.arb
@@ -661,6 +661,9 @@
"privacy":"隐私",
"display_settings": "显示设置",
"other_settings": "其他设置",
+ "require_pin_after": "之后需要 PIN",
+ "always": "总是",
+ "minutes_to_pin_code": "${minute} 分钟",
"disable_exchange": "禁用交换",
"advanced_privacy_settings": "高级隐私设置",
"settings_can_be_changed_later": "稍后可以在应用设置中更改这些设置",
@@ -669,5 +672,6 @@
"fiat_api": "法币API",
"disabled": "禁用",
"enabled": "启用",
- "tor_only": "仅限 Tor"
+ "tor_only": "仅限 Tor",
+ "unmatched_currencies": "您当前钱包的货币与扫描的 QR 的货币不匹配"
}
diff --git a/scripts/android/app_env.sh b/scripts/android/app_env.sh
index d361c8dfa..5b704eea1 100644
--- a/scripts/android/app_env.sh
+++ b/scripts/android/app_env.sh
@@ -14,14 +14,14 @@ TYPES=($MONERO_COM $CAKEWALLET $HAVEN)
APP_ANDROID_TYPE=$1
MONERO_COM_NAME="Monero.com"
-MONERO_COM_VERSION="1.2.1"
-MONERO_COM_BUILD_NUMBER=32
+MONERO_COM_VERSION="1.2.2"
+MONERO_COM_BUILD_NUMBER=33
MONERO_COM_BUNDLE_ID="com.monero.app"
MONERO_COM_PACKAGE="com.monero.app"
CAKEWALLET_NAME="Cake Wallet"
-CAKEWALLET_VERSION="4.5.1"
-CAKEWALLET_BUILD_NUMBER=136
+CAKEWALLET_VERSION="4.5.3"
+CAKEWALLET_BUILD_NUMBER=138
CAKEWALLET_BUNDLE_ID="com.cakewallet.cake_wallet"
CAKEWALLET_PACKAGE="com.cakewallet.cake_wallet"
diff --git a/scripts/ios/app_env.sh b/scripts/ios/app_env.sh
index 8bb945680..fbf8233e5 100644
--- a/scripts/ios/app_env.sh
+++ b/scripts/ios/app_env.sh
@@ -13,13 +13,13 @@ TYPES=($MONERO_COM $CAKEWALLET $HAVEN)
APP_IOS_TYPE=$1
MONERO_COM_NAME="Monero.com"
-MONERO_COM_VERSION="1.2.1"
-MONERO_COM_BUILD_NUMBER=29
+MONERO_COM_VERSION="1.2.2"
+MONERO_COM_BUILD_NUMBER=30
MONERO_COM_BUNDLE_ID="com.cakewallet.monero"
CAKEWALLET_NAME="Cake Wallet"
-CAKEWALLET_VERSION="4.5.1"
-CAKEWALLET_BUILD_NUMBER=133
+CAKEWALLET_VERSION="4.5.3"
+CAKEWALLET_BUILD_NUMBER=135
CAKEWALLET_BUNDLE_ID="com.fotolockr.cakewallet"
HAVEN_NAME="Haven"