Merge branch 'main' of https://github.com/cake-tech/cake_wallet into linux/password-direct-input

 Conflicts:
	cw_bitcoin/lib/electrum_transaction_history.dart
	lib/core/wallet_loading_service.dart
	lib/di.dart
	lib/main.dart
	lib/router.dart
	lib/src/screens/dashboard/widgets/market_place_page.dart
	lib/src/screens/wallet_list/wallet_list_page.dart
	lib/view_model/wallet_list/wallet_list_view_model.dart
	macos/Podfile.lock
	res/values/strings_ar.arb
	res/values/strings_bg.arb
	res/values/strings_cs.arb
	res/values/strings_de.arb
	res/values/strings_en.arb
	res/values/strings_es.arb
	res/values/strings_fr.arb
	res/values/strings_hi.arb
	res/values/strings_hr.arb
	res/values/strings_id.arb
	res/values/strings_it.arb
	res/values/strings_ja.arb
	res/values/strings_ko.arb
	res/values/strings_my.arb
	res/values/strings_nl.arb
	res/values/strings_pl.arb
	res/values/strings_pt.arb
	res/values/strings_ru.arb
	res/values/strings_th.arb
	res/values/strings_tr.arb
	res/values/strings_uk.arb
	res/values/strings_ur.arb
	res/values/strings_zh.arb
This commit is contained in:
OmarHatem 2023-07-13 18:26:27 +03:00
commit e9582f31e5
104 changed files with 13177 additions and 14946 deletions

View file

@ -22,7 +22,6 @@
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true" android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize" android:windowSoftInputMode="adjustResize"
android:screenOrientation="portrait"
android:exported="true"> android:exported="true">
<meta-data <meta-data
android:name="io.flutter.embedding.android.SplashScreenDrawable" android:name="io.flutter.embedding.android.SplashScreenDrawable"

View file

@ -56,9 +56,32 @@ class BitcoinWalletService extends WalletService<
} }
@override @override
Future<void> remove(String wallet) async => Future<void> remove(String wallet) async {
File(await pathForWalletDir(name: wallet, type: WalletType.bitcoin)) File(await pathForWalletDir(name: wallet, type: getType()))
.delete(recursive: true); .delete(recursive: true);
final walletInfo = walletInfoSource.values.firstWhereOrNull(
(info) => info.id == WalletBase.idFor(wallet, getType()))!;
await walletInfoSource.delete(walletInfo.key);
}
@override
Future<void> rename(String currentName, String password, String newName) async {
final currentWalletInfo = walletInfoSource.values.firstWhereOrNull(
(info) => info.id == WalletBase.idFor(currentName, getType()))!;
final currentWallet = await BitcoinWalletBase.open(
password: password,
name: currentName,
walletInfo: currentWalletInfo,
unspentCoinsInfo: unspentCoinsInfoSource);
await currentWallet.renameWalletFiles(newName);
final newWalletInfo = currentWalletInfo;
newWalletInfo.id = WalletBase.idFor(newName, getType());
newWalletInfo.name = newName;
await walletInfoSource.put(currentWalletInfo.key, newWalletInfo);
}
@override @override
Future<BitcoinWallet> restoreFromKeys( Future<BitcoinWallet> restoreFromKeys(

View file

@ -8,7 +8,7 @@ import 'package:cw_bitcoin/electrum_transaction_info.dart';
part 'electrum_transaction_history.g.dart'; part 'electrum_transaction_history.g.dart';
const _transactionsHistoryFileName = 'transactions.json'; const transactionsHistoryFileName = 'transactions.json';
class ElectrumTransactionHistory = ElectrumTransactionHistoryBase class ElectrumTransactionHistory = ElectrumTransactionHistoryBase
with _$ElectrumTransactionHistory; with _$ElectrumTransactionHistory;
@ -42,7 +42,7 @@ abstract class ElectrumTransactionHistoryBase
try { try {
final dirPath = final dirPath =
await pathForWalletDir(name: walletInfo.name, type: walletInfo.type); await pathForWalletDir(name: walletInfo.name, type: walletInfo.type);
final path = '$dirPath/$_transactionsHistoryFileName'; final path = '$dirPath/$transactionsHistoryFileName';
final data = final data =
json.encode({'height': _height, 'transactions': transactions}); json.encode({'height': _height, 'transactions': transactions});
await encryptionFileUtils.write(path: path, password: _password, data: data); await encryptionFileUtils.write(path: path, password: _password, data: data);
@ -59,7 +59,7 @@ abstract class ElectrumTransactionHistoryBase
Future<Map<String, dynamic>> _read() async { Future<Map<String, dynamic>> _read() async {
final dirPath = final dirPath =
await pathForWalletDir(name: walletInfo.name, type: walletInfo.type); await pathForWalletDir(name: walletInfo.name, type: walletInfo.type);
final path = '$dirPath/$_transactionsHistoryFileName'; final path = '$dirPath/$transactionsHistoryFileName';
final content = await encryptionFileUtils.read(path: path, password: _password); final content = await encryptionFileUtils.read(path: path, password: _password);
return json.decode(content) as Map<String, dynamic>; return json.decode(content) as Map<String, dynamic>;
} }
@ -86,4 +86,5 @@ abstract class ElectrumTransactionHistoryBase
void _update(ElectrumTransactionInfo transaction) => void _update(ElectrumTransactionInfo transaction) =>
transactions[transaction.id] = transaction; transactions[transaction.id] = transaction;
} }

View file

@ -1,5 +1,6 @@
import 'dart:async'; import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'dart:io';
import 'dart:math'; import 'dart:math';
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:cw_bitcoin/encryption_file_utils.dart'; import 'package:cw_bitcoin/encryption_file_utils.dart';
@ -439,6 +440,28 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance,
await transactionHistory.save(); await transactionHistory.save();
} }
Future<void> renameWalletFiles(String newWalletName) async {
final currentWalletPath = await pathForWallet(name: walletInfo.name, type: type);
final currentWalletFile = File(currentWalletPath);
final currentDirPath =
await pathForWalletDir(name: walletInfo.name, type: type);
final currentTransactionsFile = File('$currentDirPath/$transactionsHistoryFileName');
// Copies current wallet files into new wallet name's dir and files
if (currentWalletFile.existsSync()) {
final newWalletPath = await pathForWallet(name: newWalletName, type: type);
await currentWalletFile.copy(newWalletPath);
}
if (currentTransactionsFile.existsSync()) {
final newDirPath = await pathForWalletDir(name: newWalletName, type: type);
await currentTransactionsFile.copy('$newDirPath/$transactionsHistoryFileName');
}
// Delete old name's dir and files
await Directory(currentDirPath).delete(recursive: true);
}
@override @override
Future<void> changePassword(String password) async { Future<void> changePassword(String password) async {
_password = password; _password = password;

View file

@ -57,9 +57,32 @@ class LitecoinWalletService extends WalletService<
} }
@override @override
Future<void> remove(String wallet) async => Future<void> remove(String wallet) async {
File(await pathForWalletDir(name: wallet, type: getType())) File(await pathForWalletDir(name: wallet, type: getType()))
.delete(recursive: true); .delete(recursive: true);
final walletInfo = walletInfoSource.values.firstWhereOrNull(
(info) => info.id == WalletBase.idFor(wallet, getType()))!;
await walletInfoSource.delete(walletInfo.key);
}
@override
Future<void> rename(String currentName, String password, String newName) async {
final currentWalletInfo = walletInfoSource.values.firstWhereOrNull(
(info) => info.id == WalletBase.idFor(currentName, getType()))!;
final currentWallet = await LitecoinWalletBase.open(
password: password,
name: currentName,
walletInfo: currentWalletInfo,
unspentCoinsInfo: unspentCoinsInfoSource);
await currentWallet.renameWalletFiles(newName);
final newWalletInfo = currentWalletInfo;
newWalletInfo.id = WalletBase.idFor(newName, getType());
newWalletInfo.name = newName;
await walletInfoSource.put(currentWalletInfo.key, newWalletInfo);
}
@override @override
Future<LitecoinWallet> restoreFromKeys( Future<LitecoinWallet> restoreFromKeys(

View file

@ -14,6 +14,8 @@ abstract class TransactionHistoryBase<TransactionType extends TransactionInfo> {
void addMany(Map<String, TransactionType> transactions); void addMany(Map<String, TransactionType> transactions);
void clear() => transactions.clear();
// bool _isUpdating; // bool _isUpdating;
// @action // @action

View file

@ -17,4 +17,6 @@ abstract class WalletService<N extends WalletCredentials,
Future<bool> isWalletExit(String name); Future<bool> isWalletExit(String name);
Future<void> remove(String wallet); Future<void> remove(String wallet);
Future<void> rename(String name, String password, String newName);
} }

View file

@ -26,7 +26,7 @@ enum WalletType {
litecoin, litecoin,
@HiveField(4) @HiveField(4)
haven haven,
} }
int serializeToInt(WalletType type) { int serializeToInt(WalletType type) {
@ -77,13 +77,13 @@ String walletTypeToString(WalletType type) {
String walletTypeToDisplayName(WalletType type) { String walletTypeToDisplayName(WalletType type) {
switch (type) { switch (type) {
case WalletType.monero: case WalletType.monero:
return 'Monero'; return 'Monero (XMR)';
case WalletType.bitcoin: case WalletType.bitcoin:
return 'Bitcoin (Electrum)'; return 'Bitcoin (BTC)';
case WalletType.litecoin: case WalletType.litecoin:
return 'Litecoin (Electrum)'; return 'Litecoin (LTC)';
case WalletType.haven: case WalletType.haven:
return 'Haven'; return 'Haven (XHV)';
default: default:
return ''; return '';
} }

View file

@ -1,5 +1,7 @@
import 'dart:async'; import 'dart:async';
import 'dart:io';
import 'package:cw_core/crypto_currency.dart'; import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/pathForWallet.dart';
import 'package:cw_core/transaction_priority.dart'; import 'package:cw_core/transaction_priority.dart';
import 'package:cw_haven/haven_transaction_creation_credentials.dart'; import 'package:cw_haven/haven_transaction_creation_credentials.dart';
import 'package:cw_core/monero_amount_format.dart'; import 'package:cw_core/monero_amount_format.dart';
@ -251,6 +253,29 @@ abstract class HavenWalletBase extends WalletBase<MoneroBalance,
await haven_wallet.store(); await haven_wallet.store();
} }
Future<void> renameWalletFiles(String newWalletName) async {
final currentWalletPath = await pathForWallet(name: name, type: type);
final currentCacheFile = File(currentWalletPath);
final currentKeysFile = File('$currentWalletPath.keys');
final currentAddressListFile = File('$currentWalletPath.address.txt');
final newWalletPath = await pathForWallet(name: newWalletName, type: type);
// Copies current wallet files into new wallet name's dir and files
if (currentCacheFile.existsSync()) {
await currentCacheFile.copy(newWalletPath);
}
if (currentKeysFile.existsSync()) {
await currentKeysFile.copy('$newWalletPath.keys');
}
if (currentAddressListFile.existsSync()) {
await currentAddressListFile.copy('$newWalletPath.address.txt');
}
// Delete old name's dir and files
await Directory(currentWalletPath).delete(recursive: true);
}
@override @override
Future<void> changePassword(String password) async { Future<void> changePassword(String password) async {
haven_wallet.setPasswordSync(password); haven_wallet.setPasswordSync(password);

View file

@ -149,6 +149,26 @@ class HavenWalletService extends WalletService<
if (isExist) { if (isExist) {
await file.delete(recursive: true); await file.delete(recursive: true);
} }
final walletInfo = walletInfoSource.values
.firstWhere((info) => info.id == WalletBase.idFor(wallet, getType()));
await walletInfoSource.delete(walletInfo.key);
}
@override
Future<void> rename(
String currentName, String password, String newName) async {
final currentWalletInfo = walletInfoSource.values.firstWhere(
(info) => info.id == WalletBase.idFor(currentName, getType()));
final currentWallet = HavenWallet(walletInfo: currentWalletInfo);
await currentWallet.renameWalletFiles(newName);
final newWalletInfo = currentWalletInfo;
newWalletInfo.id = WalletBase.idFor(newName, getType());
newWalletInfo.name = newName;
await walletInfoSource.put(currentWalletInfo.key, newWalletInfo);
} }
@override @override

View file

@ -24,4 +24,5 @@ abstract class MoneroTransactionHistoryBase
@override @override
void addMany(Map<String, MoneroTransactionInfo> transactions) => void addMany(Map<String, MoneroTransactionInfo> transactions) =>
this.transactions.addAll(transactions); this.transactions.addAll(transactions);
} }

View file

@ -1,4 +1,6 @@
import 'dart:async'; import 'dart:async';
import 'dart:io';
import 'package:cw_core/pathForWallet.dart';
import 'package:cw_core/transaction_priority.dart'; import 'package:cw_core/transaction_priority.dart';
import 'package:cw_core/monero_amount_format.dart'; import 'package:cw_core/monero_amount_format.dart';
import 'package:cw_monero/monero_transaction_creation_exception.dart'; import 'package:cw_monero/monero_transaction_creation_exception.dart';
@ -6,7 +8,6 @@ import 'package:cw_monero/monero_transaction_info.dart';
import 'package:cw_monero/monero_wallet_addresses.dart'; import 'package:cw_monero/monero_wallet_addresses.dart';
import 'package:cw_core/monero_wallet_utils.dart'; import 'package:cw_core/monero_wallet_utils.dart';
import 'package:cw_monero/api/structs/pending_transaction.dart'; import 'package:cw_monero/api/structs/pending_transaction.dart';
import 'package:flutter/foundation.dart';
import 'package:mobx/mobx.dart'; import 'package:mobx/mobx.dart';
import 'package:cw_monero/api/transaction_history.dart' import 'package:cw_monero/api/transaction_history.dart'
as monero_transaction_history; as monero_transaction_history;
@ -274,6 +275,29 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
await monero_wallet.store(); await monero_wallet.store();
} }
Future<void> renameWalletFiles(String newWalletName) async {
final currentWalletPath = await pathForWallet(name: name, type: type);
final currentCacheFile = File(currentWalletPath);
final currentKeysFile = File('$currentWalletPath.keys');
final currentAddressListFile = File('$currentWalletPath.address.txt');
final newWalletPath = await pathForWallet(name: newWalletName, type: type);
// Copies current wallet files into new wallet name's dir and files
if (currentCacheFile.existsSync()) {
await currentCacheFile.copy(newWalletPath);
}
if (currentKeysFile.existsSync()) {
await currentKeysFile.copy('$newWalletPath.keys');
}
if (currentAddressListFile.existsSync()) {
await currentAddressListFile.copy('$newWalletPath.address.txt');
}
// Delete old name's dir and files
await Directory(currentWalletPath).delete(recursive: true);
}
@override @override
Future<void> changePassword(String password) async { Future<void> changePassword(String password) async {
monero_wallet.setPasswordSync(password); monero_wallet.setPasswordSync(password);

View file

@ -150,6 +150,26 @@ class MoneroWalletService extends WalletService<
if (isExist) { if (isExist) {
await file.delete(recursive: true); await file.delete(recursive: true);
} }
final walletInfo = walletInfoSource.values
.firstWhere((info) => info.id == WalletBase.idFor(wallet, getType()));
await walletInfoSource.delete(walletInfo.key);
}
@override
Future<void> rename(
String currentName, String password, String newName) async {
final currentWalletInfo = walletInfoSource.values.firstWhere(
(info) => info.id == WalletBase.idFor(currentName, getType()));
final currentWallet = MoneroWallet(walletInfo: currentWalletInfo);
await currentWallet.renameWalletFiles(newName);
final newWalletInfo = currentWalletInfo;
newWalletInfo.id = WalletBase.idFor(newName, getType());
newWalletInfo.name = newName;
await walletInfoSource.put(currentWalletInfo.key, newWalletInfo);
} }
@override @override

View file

@ -64,7 +64,6 @@ class OnRamperBuyProvider {
return Uri.https(_baseUrl, '', <String, dynamic>{ return Uri.https(_baseUrl, '', <String, dynamic>{
'apiKey': _apiKey, 'apiKey': _apiKey,
'defaultCrypto': _normalizeCryptoCurrency, 'defaultCrypto': _normalizeCryptoCurrency,
'defaultFiat': _settingsStore.fiatCurrency.title,
'wallets': '${_wallet.currency.title}:${_wallet.walletAddresses.address}', 'wallets': '${_wallet.currency.title}:${_wallet.walletAddresses.address}',
'supportSell': "false", 'supportSell': "false",
'supportSwap': "false", 'supportSwap': "false",

View file

@ -21,4 +21,11 @@ class KeyService {
await _secureStorage.write(key: key, value: encodedPassword); await _secureStorage.write(key: key, value: encodedPassword);
} }
Future<void> deleteWalletPassword({required String walletName}) async {
final key = generateStoreKeyFor(
key: SecretStoreKey.moneroWalletPassword, walletName: walletName);
await _secureStorage.delete(key: key);
}
} }

View file

@ -7,43 +7,58 @@ import 'package:cw_core/wallet_type.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
class WalletLoadingService { class WalletLoadingService {
WalletLoadingService( WalletLoadingService(
this.sharedPreferences, this.sharedPreferences, this.keyService, this.walletServiceFactory);
this.keyService,
this.walletServiceFactory);
final SharedPreferences sharedPreferences; final SharedPreferences sharedPreferences;
final KeyService keyService; final KeyService keyService;
final WalletService Function(WalletType type) walletServiceFactory; final WalletService Function(WalletType type) walletServiceFactory;
Future<void> renameWallet(
WalletType type, String name, String newName, {String? password}) async {
final walletService = walletServiceFactory.call(type);
final walletPassword = password ?? (await keyService.getWalletPassword(walletName: name));
// Save the current wallet's password to the new wallet name's key
await keyService.saveWalletPassword(
walletName: newName, password: walletPassword);
// Delete previous wallet name from keyService to keep only new wallet's name
// otherwise keeps duplicate (old and new names)
await keyService.deleteWalletPassword(walletName: name);
await walletService.rename(name, walletPassword, newName);
}
Future<WalletBase> load(WalletType type, String name, {String? password}) async { Future<WalletBase> load(WalletType type, String name, {String? password}) async {
final walletService = walletServiceFactory.call(type); final walletService = walletServiceFactory.call(type);
final walletPassword = password ?? (await keyService.getWalletPassword(walletName: name)); final walletPassword = password ?? (await keyService.getWalletPassword(walletName: name));
final wallet = await walletService.openWallet(name, walletPassword); final wallet = await walletService.openWallet(name, walletPassword);
if (type == WalletType.monero) { if (type == WalletType.monero) {
await updateMoneroWalletPassword(wallet); await updateMoneroWalletPassword(wallet);
} }
return wallet; return wallet;
} }
Future<void> updateMoneroWalletPassword(WalletBase wallet) async { Future<void> updateMoneroWalletPassword(WalletBase wallet) async {
final key = PreferencesKey.moneroWalletUpdateV1Key(wallet.name); final key = PreferencesKey.moneroWalletUpdateV1Key(wallet.name);
var isPasswordUpdated = sharedPreferences.getBool(key) ?? false; var isPasswordUpdated = sharedPreferences.getBool(key) ?? false;
if (isPasswordUpdated) { if (isPasswordUpdated) {
return; return;
} }
final password = generateWalletPassword(); final password = generateWalletPassword();
// Save new generated password with backup key for case where // Save new generated password with backup key for case where
// wallet will change password, but it will fail to update in secure storage // wallet will change password, but it will fail to update in secure storage
final bakWalletName = '#__${wallet.name}_bak__#'; final bakWalletName = '#__${wallet.name}_bak__#';
await keyService.saveWalletPassword(walletName: bakWalletName, password: password); await keyService.saveWalletPassword(
await wallet.changePassword(password); walletName: bakWalletName, password: password);
await keyService.saveWalletPassword(walletName: wallet.name, password: password); await wallet.changePassword(password);
isPasswordUpdated = true; await keyService.saveWalletPassword(
await sharedPreferences.setBool(key, isPasswordUpdated); walletName: wallet.name, password: password);
} isPasswordUpdated = true;
await sharedPreferences.setBool(key, isPasswordUpdated);
}
} }

View file

@ -43,6 +43,7 @@ import 'package:cake_wallet/src/screens/setup_2fa/modify_2fa_page.dart';
import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa_qr_page.dart'; import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa_qr_page.dart';
import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa.dart'; import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa.dart';
import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa_enter_code_page.dart'; import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa_enter_code_page.dart';
import 'package:cake_wallet/src/screens/wallet/wallet_edit_page.dart';
import 'package:cake_wallet/themes/theme_list.dart'; import 'package:cake_wallet/themes/theme_list.dart';
import 'package:cake_wallet/utils/device_info.dart'; import 'package:cake_wallet/utils/device_info.dart';
import 'package:cake_wallet/store/anonpay/anonpay_transactions_store.dart'; import 'package:cake_wallet/store/anonpay/anonpay_transactions_store.dart';
@ -79,6 +80,8 @@ import 'package:cake_wallet/view_model/settings/privacy_settings_view_model.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:cake_wallet/view_model/advanced_privacy_settings_view_model.dart'; import 'package:cake_wallet/view_model/advanced_privacy_settings_view_model.dart';
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_item.dart'; import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_item.dart';
import 'package:cake_wallet/view_model/wallet_list/wallet_edit_view_model.dart';
import 'package:cake_wallet/view_model/wallet_list/wallet_list_item.dart';
import 'package:cake_wallet/view_model/wallet_unlock_loadable_view_model.dart'; import 'package:cake_wallet/view_model/wallet_unlock_loadable_view_model.dart';
import 'package:cake_wallet/view_model/wallet_unlock_verifiable_view_model.dart'; import 'package:cake_wallet/view_model/wallet_unlock_verifiable_view_model.dart';
import 'package:cake_wallet/view_model/wallet_unlock_view_model.dart'; import 'package:cake_wallet/view_model/wallet_unlock_view_model.dart';
@ -582,6 +585,21 @@ Future setup({
authService: getIt.get<AuthService>(), authService: getIt.get<AuthService>(),
)); ));
getIt.registerFactoryParam<WalletEditViewModel, WalletListViewModel, void>(
(WalletListViewModel walletListViewModel, _) => WalletEditViewModel(
walletListViewModel, getIt.get<WalletLoadingService>()));
getIt.registerFactoryParam<WalletEditPage, List<dynamic>, void>((args, _) {
final walletListViewModel = args.first as WalletListViewModel;
final editingWallet = args.last as WalletListItem;
return WalletEditPage(
walletEditViewModel: getIt.get<WalletEditViewModel>(param1: walletListViewModel),
authService: getIt.get<AuthService>(),
walletNewVM: getIt.get<WalletNewVM>(param1: editingWallet.type),
editingWallet: editingWallet);
});
getIt.registerFactory(() { getIt.registerFactory(() {
final wallet = getIt.get<AppStore>().wallet!; final wallet = getIt.get<AppStore>().wallet!;

View file

@ -1,6 +1,10 @@
import 'dart:convert'; import 'dart:convert';
import 'dart:io';
import 'package:cake_wallet/exchange/trade_not_found_exeption.dart'; import 'package:cake_wallet/exchange/trade_not_found_exeption.dart';
import 'package:cake_wallet/store/settings_store.dart';
import 'package:cake_wallet/utils/device_info.dart'; import 'package:cake_wallet/utils/device_info.dart';
import 'package:cake_wallet/utils/distribution_info.dart';
import 'package:cake_wallet/wallet_type_utils.dart';
import 'package:http/http.dart'; import 'package:http/http.dart';
import 'package:cake_wallet/.secrets.g.dart' as secrets; import 'package:cake_wallet/.secrets.g.dart' as secrets;
import 'package:cw_core/crypto_currency.dart'; import 'package:cw_core/crypto_currency.dart';
@ -14,7 +18,7 @@ import 'package:cake_wallet/exchange/changenow/changenow_request.dart';
import 'package:cake_wallet/exchange/exchange_provider_description.dart'; import 'package:cake_wallet/exchange/exchange_provider_description.dart';
class ChangeNowExchangeProvider extends ExchangeProvider { class ChangeNowExchangeProvider extends ExchangeProvider {
ChangeNowExchangeProvider() ChangeNowExchangeProvider({required this.settingsStore})
: _lastUsedRateId = '', : _lastUsedRateId = '',
super( super(
pairList: CryptoCurrency.all pairList: CryptoCurrency.all
@ -25,7 +29,8 @@ class ChangeNowExchangeProvider extends ExchangeProvider {
.expand((i) => i) .expand((i) => i)
.toList()); .toList());
static final apiKey = DeviceInfo.instance.isMobile ? secrets.changeNowApiKey : secrets.changeNowApiKeyDesktop; static final apiKey =
DeviceInfo.instance.isMobile ? secrets.changeNowApiKey : secrets.changeNowApiKeyDesktop;
static const apiAuthority = 'api.changenow.io'; static const apiAuthority = 'api.changenow.io';
static const createTradePath = '/v2/exchange'; static const createTradePath = '/v2/exchange';
static const findTradeByIdPath = '/v2/exchange/by-id'; static const findTradeByIdPath = '/v2/exchange/by-id';
@ -46,21 +51,22 @@ class ChangeNowExchangeProvider extends ExchangeProvider {
bool get supportsFixedRate => true; bool get supportsFixedRate => true;
@override @override
ExchangeProviderDescription get description => ExchangeProviderDescription get description => ExchangeProviderDescription.changeNow;
ExchangeProviderDescription.changeNow;
@override @override
Future<bool> checkIsAvailable() async => true; Future<bool> checkIsAvailable() async => true;
final SettingsStore settingsStore;
String _lastUsedRateId; String _lastUsedRateId;
static String getFlow(bool isFixedRate) => isFixedRate ? 'fixed-rate' : 'standard'; static String getFlow(bool isFixedRate) => isFixedRate ? 'fixed-rate' : 'standard';
@override @override
Future<Limits> fetchLimits({ Future<Limits> fetchLimits(
required CryptoCurrency from, {required CryptoCurrency from,
required CryptoCurrency to, required CryptoCurrency to,
required bool isFixedRateMode}) async { required bool isFixedRateMode}) async {
final headers = {apiHeaderKey: apiKey}; final headers = {apiHeaderKey: apiKey};
final normalizedFrom = normalizeCryptoCurrency(from); final normalizedFrom = normalizeCryptoCurrency(from);
final normalizedTo = normalizeCryptoCurrency(to); final normalizedTo = normalizeCryptoCurrency(to);
@ -70,7 +76,8 @@ class ChangeNowExchangeProvider extends ExchangeProvider {
'toCurrency': normalizedTo, 'toCurrency': normalizedTo,
'fromNetwork': networkFor(from), 'fromNetwork': networkFor(from),
'toNetwork': networkFor(to), 'toNetwork': networkFor(to),
'flow': flow}; 'flow': flow
};
final uri = Uri.https(apiAuthority, rangePath, params); final uri = Uri.https(apiAuthority, rangePath, params);
final response = await get(uri, headers: headers); final response = await get(uri, headers: headers);
@ -87,19 +94,24 @@ class ChangeNowExchangeProvider extends ExchangeProvider {
final responseJSON = json.decode(response.body) as Map<String, dynamic>; final responseJSON = json.decode(response.body) as Map<String, dynamic>;
return Limits( return Limits(
min: responseJSON['minAmount'] as double?, min: responseJSON['minAmount'] as double?, max: responseJSON['maxAmount'] as double?);
max: responseJSON['maxAmount'] as double?);
} }
@override @override
Future<Trade> createTrade({required TradeRequest request, required bool isFixedRateMode}) async { Future<Trade> createTrade({required TradeRequest request, required bool isFixedRateMode}) async {
final _request = request as ChangeNowRequest; final _request = request as ChangeNowRequest;
final headers = { final distributionPath = await DistributionInfo.instance.getDistributionPath();
apiHeaderKey: apiKey, final formattedAppVersion = int.tryParse(settingsStore.appVersion.replaceAll('.', '')) ?? 0;
'Content-Type': 'application/json'}; final payload = {
'app': isMoneroOnly ? 'monerocom' : 'cakewallet',
'device': Platform.operatingSystem,
'distribution': distributionPath,
'version': formattedAppVersion
};
final headers = {apiHeaderKey: apiKey, 'Content-Type': 'application/json'};
final flow = getFlow(isFixedRateMode); final flow = getFlow(isFixedRateMode);
final type = isFixedRateMode ? 'reverse' : 'direct'; final type = isFixedRateMode ? 'reverse' : 'direct';
final body = <String, String>{ final body = <String, dynamic>{
'fromCurrency': normalizeCryptoCurrency(_request.from), 'fromCurrency': normalizeCryptoCurrency(_request.from),
'toCurrency': normalizeCryptoCurrency(_request.to), 'toCurrency': normalizeCryptoCurrency(_request.to),
'fromNetwork': networkFor(_request.from), 'fromNetwork': networkFor(_request.from),
@ -109,7 +121,8 @@ class ChangeNowExchangeProvider extends ExchangeProvider {
'address': _request.address, 'address': _request.address,
'flow': flow, 'flow': flow,
'type': type, 'type': type,
'refundAddress': _request.refundAddress 'refundAddress': _request.refundAddress,
'payload': payload,
}; };
if (isFixedRateMode) { if (isFixedRateMode) {
@ -164,7 +177,7 @@ class ChangeNowExchangeProvider extends ExchangeProvider {
Future<Trade> findTradeById({required String id}) async { Future<Trade> findTradeById({required String id}) async {
final headers = {apiHeaderKey: apiKey}; final headers = {apiHeaderKey: apiKey};
final params = <String, String>{'id': id}; final params = <String, String>{'id': id};
final uri = Uri.https(apiAuthority,findTradeByIdPath, params); final uri = Uri.https(apiAuthority, findTradeByIdPath, params);
final response = await get(uri, headers: headers); final response = await get(uri, headers: headers);
if (response.statusCode == 404) { if (response.statusCode == 404) {
@ -175,8 +188,7 @@ class ChangeNowExchangeProvider extends ExchangeProvider {
final responseJSON = json.decode(response.body) as Map<String, dynamic>; final responseJSON = json.decode(response.body) as Map<String, dynamic>;
final error = responseJSON['message'] as String; final error = responseJSON['message'] as String;
throw TradeNotFoundException(id, throw TradeNotFoundException(id, provider: description, description: error);
provider: description, description: error);
} }
if (response.statusCode != 200) { if (response.statusCode != 200) {
@ -234,7 +246,8 @@ class ChangeNowExchangeProvider extends ExchangeProvider {
'fromNetwork': networkFor(from), 'fromNetwork': networkFor(from),
'toNetwork': networkFor(to), 'toNetwork': networkFor(to),
'type': type, 'type': type,
'flow': flow}; 'flow': flow
};
if (isReverse) { if (isReverse) {
params['toAmount'] = amount.toString(); params['toAmount'] = amount.toString();
@ -246,7 +259,7 @@ class ChangeNowExchangeProvider extends ExchangeProvider {
final response = await get(uri, headers: headers); final response = await get(uri, headers: headers);
final responseJSON = json.decode(response.body) as Map<String, dynamic>; final responseJSON = json.decode(response.body) as Map<String, dynamic>;
final fromAmount = double.parse(responseJSON['fromAmount'].toString()); final fromAmount = double.parse(responseJSON['fromAmount'].toString());
final toAmount = double.parse(responseJSON['toAmount'].toString()); final toAmount = double.parse(responseJSON['toAmount'].toString());
final rateId = responseJSON['rateId'] as String? ?? ''; final rateId = responseJSON['rateId'] as String? ?? '';
if (rateId.isNotEmpty) { if (rateId.isNotEmpty) {
@ -254,7 +267,7 @@ class ChangeNowExchangeProvider extends ExchangeProvider {
} }
return isReverse ? (amount / fromAmount) : (toAmount / amount); return isReverse ? (amount / fromAmount) : (toAmount / amount);
} catch(e) { } catch (e) {
print(e.toString()); print(e.toString());
return 0.0; return 0.0;
} }
@ -265,24 +278,20 @@ class ChangeNowExchangeProvider extends ExchangeProvider {
case CryptoCurrency.usdt: case CryptoCurrency.usdt:
return CryptoCurrency.btc.title.toLowerCase(); return CryptoCurrency.btc.title.toLowerCase();
default: default:
return currency.tag != null return currency.tag != null ? currency.tag!.toLowerCase() : currency.title.toLowerCase();
? currency.tag!.toLowerCase()
: currency.title.toLowerCase();
}
} }
} }
}
String normalizeCryptoCurrency(CryptoCurrency currency) { String normalizeCryptoCurrency(CryptoCurrency currency) {
switch(currency) { switch (currency) {
case CryptoCurrency.zec: case CryptoCurrency.zec:
return 'zec'; return 'zec';
case CryptoCurrency.usdcpoly: case CryptoCurrency.usdcpoly:
return 'usdcmatic'; return 'usdcmatic';
case CryptoCurrency.maticpoly: case CryptoCurrency.maticpoly:
return 'maticmainnet'; return 'maticmainnet';
default: default:
return currency.title.toLowerCase(); return currency.title.toLowerCase();
}
} }
}

View file

@ -194,12 +194,7 @@ class App extends StatefulWidget {
} }
class AppState extends State<App> with SingleTickerProviderStateMixin { class AppState extends State<App> with SingleTickerProviderStateMixin {
AppState() : yatStore = getIt.get<YatStore>() { AppState() : yatStore = getIt.get<YatStore>();
SystemChrome.setPreferredOrientations(
ResponsiveLayoutUtil.instance.isIpad ?
[DeviceOrientation.landscapeLeft, DeviceOrientation.landscapeRight] :
[DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]);
}
YatStore yatStore; YatStore yatStore;
StreamSubscription? stream; StreamSubscription? stream;
@ -291,7 +286,43 @@ class AppState extends State<App> with SingleTickerProviderStateMixin {
locale: Locale(settingsStore.languageCode), locale: Locale(settingsStore.languageCode),
onGenerateRoute: (settings) => Router.createRoute(settings), onGenerateRoute: (settings) => Router.createRoute(settings),
initialRoute: initialRoute, initialRoute: initialRoute,
home: _Home(),
)); ));
}); });
} }
} }
class _Home extends StatefulWidget {
const _Home();
@override
State<_Home> createState() => _HomeState();
}
class _HomeState extends State<_Home> {
@override
void didChangeDependencies() {
if(!ResponsiveLayoutUtil.instance.isMobile){
_setOrientation(context);
}
super.didChangeDependencies();
}
void _setOrientation(BuildContext context){
final orientation = MediaQuery.of(context).orientation;
final width = MediaQuery.of(context).size.width;
final height = MediaQuery.of(context).size.height;
if (orientation == Orientation.portrait && width < height) {
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]);
} else if (orientation == Orientation.landscape && width > height) {
SystemChrome.setPreferredOrientations([DeviceOrientation.landscapeLeft, DeviceOrientation.landscapeRight]);
}
}
@override
Widget build(BuildContext context) {
return const SizedBox.shrink();
}
}

View file

@ -47,6 +47,7 @@ import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
import 'package:cake_wallet/view_model/monero_account_list/account_list_item.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/node_list/node_create_or_edit_view_model.dart';
import 'package:cake_wallet/view_model/advanced_privacy_settings_view_model.dart'; import 'package:cake_wallet/view_model/advanced_privacy_settings_view_model.dart';
import 'package:cake_wallet/view_model/wallet_list/wallet_list_item.dart';
import 'package:cake_wallet/wallet_type_utils.dart'; import 'package:cake_wallet/wallet_type_utils.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -65,6 +66,7 @@ import 'package:cake_wallet/src/screens/nodes/node_create_or_edit_page.dart';
import 'package:cake_wallet/src/screens/receive/receive_page.dart'; import 'package:cake_wallet/src/screens/receive/receive_page.dart';
import 'package:cake_wallet/src/screens/subaddress/address_edit_or_create_page.dart'; import 'package:cake_wallet/src/screens/subaddress/address_edit_or_create_page.dart';
import 'package:cake_wallet/src/screens/wallet_list/wallet_list_page.dart'; import 'package:cake_wallet/src/screens/wallet_list/wallet_list_page.dart';
import 'package:cake_wallet/src/screens/wallet/wallet_edit_page.dart';
import 'package:cake_wallet/src/screens/new_wallet/new_wallet_page.dart'; import 'package:cake_wallet/src/screens/new_wallet/new_wallet_page.dart';
import 'package:cake_wallet/src/screens/setup_pin_code/setup_pin_code.dart'; import 'package:cake_wallet/src/screens/setup_pin_code/setup_pin_code.dart';
import 'package:cake_wallet/src/screens/restore/restore_options_page.dart'; import 'package:cake_wallet/src/screens/restore/restore_options_page.dart';
@ -275,6 +277,12 @@ Route<dynamic> createRoute(RouteSettings settings) {
return MaterialPageRoute<void>( return MaterialPageRoute<void>(
fullscreenDialog: true, builder: (_) => getIt.get<WalletListPage>()); fullscreenDialog: true, builder: (_) => getIt.get<WalletListPage>());
case Routes.walletEdit:
return MaterialPageRoute<void>(
fullscreenDialog: true,
builder: (_) => getIt.get<WalletEditPage>(
param1: settings.arguments as List<dynamic>));
case Routes.auth: case Routes.auth:
return MaterialPageRoute<void>( return MaterialPageRoute<void>(
fullscreenDialog: true, fullscreenDialog: true,

View file

@ -11,6 +11,7 @@ class Routes {
static const transactionDetails = '/transaction_info'; static const transactionDetails = '/transaction_info';
static const receive = '/receive'; static const receive = '/receive';
static const newSubaddress = '/new_subaddress'; static const newSubaddress = '/new_subaddress';
static const walletEdit = '/walletEdit';
static const disclaimer = '/disclaimer'; static const disclaimer = '/disclaimer';
static const readDisclaimer = '/read_disclaimer'; static const readDisclaimer = '/read_disclaimer';
static const seedLanguage = '/seed_language'; static const seedLanguage = '/seed_language';

View file

@ -5,6 +5,7 @@ import 'package:cake_wallet/palette.dart';
import 'package:cake_wallet/di.dart'; import 'package:cake_wallet/di.dart';
import 'package:cake_wallet/store/settings_store.dart'; import 'package:cake_wallet/store/settings_store.dart';
import 'package:cake_wallet/src/widgets/nav_bar.dart'; import 'package:cake_wallet/src/widgets/nav_bar.dart';
import 'package:cake_wallet/generated/i18n.dart';
enum AppBarStyle { regular, withShadow, transparent } enum AppBarStyle { regular, withShadow, transparent }
@ -58,7 +59,7 @@ abstract class BasePage extends StatelessWidget {
child: ButtonTheme( child: ButtonTheme(
minWidth: double.minPositive, minWidth: double.minPositive,
child: Semantics( child: Semantics(
label: 'Back', label: S.of(context).seed_alert_back,
child: TextButton( child: TextButton(
style: ButtonStyle( style: ButtonStyle(
overlayColor: MaterialStateColor.resolveWith( overlayColor: MaterialStateColor.resolveWith(

View file

@ -24,35 +24,40 @@ class ContactListPage extends BasePage {
@override @override
Widget? trailing(BuildContext context) { Widget? trailing(BuildContext context) {
return Container( return MergeSemantics(
width: 32.0, child: Container(
height: 32.0, width: 32.0,
decoration: BoxDecoration( height: 32.0,
shape: BoxShape.circle, decoration: BoxDecoration(
color: Theme.of(context) shape: BoxShape.circle,
color: Theme.of(context)
.accentTextTheme! .accentTextTheme!
.bodySmall! .bodySmall!
.color!), .color!),
child: Stack( child: Stack(
alignment: Alignment.center, alignment: Alignment.center,
children: <Widget>[ children: <Widget>[
Icon(Icons.add, Icon(Icons.add,
color: Theme.of(context).primaryTextTheme!.titleLarge!.color!, color: Theme.of(context).primaryTextTheme!.titleLarge!.color!,
size: 22.0), size: 22.0),
ButtonTheme( ButtonTheme(
minWidth: 32.0, minWidth: 32.0,
height: 32.0, height: 32.0,
child: TextButton( child: Semantics(
// FIX-ME: Style label: S.of(context).add,
//shape: CircleBorder(), child: TextButton(
onPressed: () async { // FIX-ME: Style
await Navigator.of(context) //shape: CircleBorder(),
.pushNamed(Routes.addressBookAddContact); onPressed: () async {
}, await Navigator.of(context)
child: Offstage()), .pushNamed(Routes.addressBookAddContact);
) },
], child: Offstage()),
)); ),
)
],
)),
);
} }
@override @override

View file

@ -4,6 +4,7 @@ import 'package:cake_wallet/di.dart';
import 'package:cake_wallet/entities/main_actions.dart'; import 'package:cake_wallet/entities/main_actions.dart';
import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/desktop_sidebar_wrapper.dart'; import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/desktop_sidebar_wrapper.dart';
import 'package:cake_wallet/src/screens/dashboard/widgets/market_place_page.dart'; import 'package:cake_wallet/src/screens/dashboard/widgets/market_place_page.dart';
import 'package:cake_wallet/utils/device_info.dart';
import 'package:cake_wallet/utils/version_comparator.dart'; import 'package:cake_wallet/utils/version_comparator.dart';
import 'package:cake_wallet/view_model/dashboard/market_place_view_model.dart'; import 'package:cake_wallet/view_model/dashboard/market_place_view_model.dart';
import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/generated/i18n.dart';
@ -42,15 +43,30 @@ class DashboardPage extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(body: LayoutBuilder(
body: ResponsiveLayoutUtil.instance.isMobile builder: (context, constraints) {
? _DashboardPageView( if (DeviceInfo.instance.isDesktop) {
if (constraints.maxWidth > ResponsiveLayoutUtil.kDesktopMaxDashBoardWidthConstraint) {
return getIt.get<DesktopSidebarWrapper>();
} else {
return _DashboardPageView(
balancePage: balancePage, balancePage: balancePage,
dashboardViewModel: dashboardViewModel, dashboardViewModel: dashboardViewModel,
addressListViewModel: addressListViewModel, addressListViewModel: addressListViewModel,
) );
: getIt.get<DesktopSidebarWrapper>(), }
); } else if (ResponsiveLayoutUtil.instance.shouldRenderMobileUI()) {
return _DashboardPageView(
balancePage: balancePage,
dashboardViewModel: dashboardViewModel,
addressListViewModel: addressListViewModel,
);
} else {
return getIt.get<DesktopSidebarWrapper>();
}
},
));
} }
} }
@ -111,7 +127,8 @@ class _DashboardPageView extends BasePage {
//splashColor: Colors.transparent, //splashColor: Colors.transparent,
//padding: EdgeInsets.all(0), //padding: EdgeInsets.all(0),
onPressed: () => onOpenEndDrawer(), onPressed: () => onOpenEndDrawer(),
child: Semantics(label: 'Menu', child: menuButton))); child: Semantics(
label: S.of(context).wallet_menu, child: menuButton)));
} }
final DashboardViewModel dashboardViewModel; final DashboardViewModel dashboardViewModel;
@ -248,7 +265,7 @@ class _DashboardPageView extends BasePage {
} }
if (dashboardViewModel.shouldShowMarketPlaceInDashboard) { if (dashboardViewModel.shouldShowMarketPlaceInDashboard) {
pages.add(Semantics( pages.add(Semantics(
label: 'Marketplace Page', label: S.of(context).market_place,
child: MarketPlacePage( child: MarketPlacePage(
dashboardViewModel: dashboardViewModel, dashboardViewModel: dashboardViewModel,
marketPlaceViewModel: getIt.get<MarketPlaceViewModel>(), marketPlaceViewModel: getIt.get<MarketPlaceViewModel>(),
@ -256,9 +273,9 @@ class _DashboardPageView extends BasePage {
), ),
); );
} }
pages.add(Semantics(label: 'Balance Page', child: balancePage)); pages.add(Semantics(label: S.of(context).balance_page, child: balancePage));
pages.add(Semantics( pages.add(Semantics(
label: 'Transactions Page', label: S.of(context).settings_transactions,
child: TransactionsPage(dashboardViewModel: dashboardViewModel))); child: TransactionsPage(dashboardViewModel: dashboardViewModel)));
_isEffectsInstalled = true; _isEffectsInstalled = true;

View file

@ -14,72 +14,75 @@ class DesktopDashboardActions extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Observer( return Container(
builder: (_) { color: Theme.of(context).colorScheme.background,
return Column( child: Observer(
children: [ builder: (_) {
const SizedBox(height: 16), return Column(
DesktopActionButton( children: [
title: MainActions.exchangeAction.name(context), const SizedBox(height: 16),
image: MainActions.exchangeAction.image, DesktopActionButton(
canShow: MainActions.exchangeAction.canShow?.call(dashboardViewModel), title: MainActions.exchangeAction.name(context),
isEnabled: MainActions.exchangeAction.isEnabled?.call(dashboardViewModel), image: MainActions.exchangeAction.image,
onTap: () async => await MainActions.exchangeAction.onTap(context, dashboardViewModel), canShow: MainActions.exchangeAction.canShow?.call(dashboardViewModel),
), isEnabled: MainActions.exchangeAction.isEnabled?.call(dashboardViewModel),
Row( onTap: () async => await MainActions.exchangeAction.onTap(context, dashboardViewModel),
children: [
Expanded(
child: DesktopActionButton(
title: MainActions.receiveAction.name(context),
image: MainActions.receiveAction.image,
canShow: MainActions.receiveAction.canShow?.call(dashboardViewModel),
isEnabled: MainActions.receiveAction.isEnabled?.call(dashboardViewModel),
onTap: () async =>
await MainActions.receiveAction.onTap(context, dashboardViewModel),
),
),
Expanded(
child: DesktopActionButton(
title: MainActions.sendAction.name(context),
image: MainActions.sendAction.image,
canShow: MainActions.sendAction.canShow?.call(dashboardViewModel),
isEnabled: MainActions.sendAction.isEnabled?.call(dashboardViewModel),
onTap: () async => await MainActions.sendAction.onTap(context, dashboardViewModel),
),
),
],
),
Row(
children: [
Expanded(
child: DesktopActionButton(
title: MainActions.buyAction.name(context),
image: MainActions.buyAction.image,
canShow: MainActions.buyAction.canShow?.call(dashboardViewModel),
isEnabled: MainActions.buyAction.isEnabled?.call(dashboardViewModel),
onTap: () async => await MainActions.buyAction.onTap(context, dashboardViewModel),
),
),
Expanded(
child: DesktopActionButton(
title: MainActions.sellAction.name(context),
image: MainActions.sellAction.image,
canShow: MainActions.sellAction.canShow?.call(dashboardViewModel),
isEnabled: MainActions.sellAction.isEnabled?.call(dashboardViewModel),
onTap: () async => await MainActions.sellAction.onTap(context, dashboardViewModel),
),
),
],
),
Expanded(
child: MarketPlacePage(
dashboardViewModel: dashboardViewModel,
marketPlaceViewModel: getIt.get<MarketPlaceViewModel>(),
), ),
), Row(
], children: [
); Expanded(
} child: DesktopActionButton(
title: MainActions.receiveAction.name(context),
image: MainActions.receiveAction.image,
canShow: MainActions.receiveAction.canShow?.call(dashboardViewModel),
isEnabled: MainActions.receiveAction.isEnabled?.call(dashboardViewModel),
onTap: () async =>
await MainActions.receiveAction.onTap(context, dashboardViewModel),
),
),
Expanded(
child: DesktopActionButton(
title: MainActions.sendAction.name(context),
image: MainActions.sendAction.image,
canShow: MainActions.sendAction.canShow?.call(dashboardViewModel),
isEnabled: MainActions.sendAction.isEnabled?.call(dashboardViewModel),
onTap: () async => await MainActions.sendAction.onTap(context, dashboardViewModel),
),
),
],
),
Row(
children: [
Expanded(
child: DesktopActionButton(
title: MainActions.buyAction.name(context),
image: MainActions.buyAction.image,
canShow: MainActions.buyAction.canShow?.call(dashboardViewModel),
isEnabled: MainActions.buyAction.isEnabled?.call(dashboardViewModel),
onTap: () async => await MainActions.buyAction.onTap(context, dashboardViewModel),
),
),
Expanded(
child: DesktopActionButton(
title: MainActions.sellAction.name(context),
image: MainActions.sellAction.image,
canShow: MainActions.sellAction.canShow?.call(dashboardViewModel),
isEnabled: MainActions.sellAction.isEnabled?.call(dashboardViewModel),
onTap: () async => await MainActions.sellAction.onTap(context, dashboardViewModel),
),
),
],
),
Expanded(
child: MarketPlacePage(
dashboardViewModel: dashboardViewModel,
marketPlaceViewModel: getIt.get<MarketPlaceViewModel>(),
),
),
],
);
}
),
); );
} }
} }

View file

@ -77,27 +77,21 @@ class DesktopSidebarWrapper extends BasePage {
SideMenuItem( SideMenuItem(
imagePath: 'assets/images/wallet_outline.png', imagePath: 'assets/images/wallet_outline.png',
isSelected: desktopSidebarViewModel.currentPage == SidebarItem.dashboard, isSelected: desktopSidebarViewModel.currentPage == SidebarItem.dashboard,
onTap: () => desktopSidebarViewModel.onPageChange(SidebarItem.dashboard), onTap: () {
desktopSidebarViewModel.onPageChange(SidebarItem.dashboard);
desktopNavigatorKey.currentState
?.pushNamedAndRemoveUntil(Routes.desktop_actions, (route) => false);
},
), ),
SideMenuItem( SideMenuItem(
onTap: () { onTap: () {
String? currentPath; if (desktopSidebarViewModel.currentPage == SidebarItem.transactions) {
desktopNavigatorKey.currentState
desktopNavigatorKey.currentState?.popUntil((route) { ?.pushNamedAndRemoveUntil(Routes.desktop_actions, (route) => false);
currentPath = route.settings.name; desktopSidebarViewModel.resetSidebar();
return true; } else {
}); desktopSidebarViewModel.onPageChange(SidebarItem.transactions);
desktopNavigatorKey.currentState?.pushNamed(Routes.transactionsPage);
switch (currentPath) {
case Routes.transactionsPage:
desktopSidebarViewModel.resetSidebar();
break;
default:
desktopSidebarViewModel.resetSidebar();
Future.delayed(Duration(milliseconds: 10), () {
desktopSidebarViewModel.onPageChange(SidebarItem.transactions);
desktopNavigatorKey.currentState?.pushNamed(Routes.transactionsPage);
});
} }
}, },
isSelected: desktopSidebarViewModel.currentPage == SidebarItem.transactions, isSelected: desktopSidebarViewModel.currentPage == SidebarItem.transactions,
@ -156,20 +150,11 @@ class DesktopSidebarWrapper extends BasePage {
void _setEffects() async { void _setEffects() async {
reaction<SidebarItem>((_) => desktopSidebarViewModel.currentPage, (page) { reaction<SidebarItem>((_) => desktopSidebarViewModel.currentPage, (page) {
String? currentPath; if (page == SidebarItem.dashboard) {
pageController.jumpToPage(0);
desktopNavigatorKey.currentState?.popUntil((route) {
currentPath = route.settings.name;
return true;
});
if (page == SidebarItem.transactions) {
return; return;
} }
pageController.jumpToPage(page.index - 1);
if (currentPath == Routes.transactionsPage) {
Navigator.of(desktopNavigatorKey.currentContext!).pop();
}
pageController.jumpToPage(page.index);
}); });
} }
} }

View file

@ -82,7 +82,9 @@ class AddressPage extends BasePage {
child: ButtonTheme( child: ButtonTheme(
minWidth: double.minPositive, minWidth: double.minPositive,
child: Semantics( child: Semantics(
label: !isMobileView ? 'Close' : 'Back', label: !isMobileView
? S.of(context).close
: S.of(context).seed_alert_back,
child: TextButton( child: TextButton(
style: ButtonStyle( style: ButtonStyle(
overlayColor: MaterialStateColor.resolveWith( overlayColor: MaterialStateColor.resolveWith(

View file

@ -1,6 +1,9 @@
import 'package:cake_wallet/src/screens/exchange_trade/information_page.dart';
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:cake_wallet/utils/feature_flag.dart';
import 'package:cake_wallet/utils/responsive_layout_util.dart'; import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.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_mobx/flutter_mobx.dart'; import 'package:flutter_mobx/flutter_mobx.dart';
@ -14,9 +17,6 @@ class BalancePage extends StatelessWidget {
final DashboardViewModel dashboardViewModel; final DashboardViewModel dashboardViewModel;
final SettingsStore settingsStore; final SettingsStore settingsStore;
Color get backgroundLightColor =>
settingsStore.currentTheme.type == ThemeType.bright ? Colors.transparent : Colors.white;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return GestureDetector( return GestureDetector(
@ -44,7 +44,7 @@ class BalancePage extends StatelessWidget {
textAlign: TextAlign.center); textAlign: TextAlign.center);
})), })),
Observer(builder: (_) { Observer(builder: (_) {
if (dashboardViewModel.balanceViewModel.isShowCard) { if (dashboardViewModel.balanceViewModel.isShowCard && FeatureFlag.isCakePayEnabled) {
return IntroducingCard( return IntroducingCard(
title: S.of(context).introducing_cake_pay, title: S.of(context).introducing_cake_pay,
subTitle: S.of(context).cake_pay_learn_more, subTitle: S.of(context).cake_pay_learn_more,
@ -104,63 +104,86 @@ class BalancePage extends StatelessWidget {
color: Theme.of(context).textTheme!.titleLarge!.backgroundColor!), color: Theme.of(context).textTheme!.titleLarge!.backgroundColor!),
child: Container( child: Container(
margin: const EdgeInsets.only(top: 16, left: 24, right: 24, bottom: 24), margin: const EdgeInsets.only(top: 16, left: 24, right: 24, bottom: 24),
child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ child: Column(
SizedBox( crossAxisAlignment: CrossAxisAlignment.start,
height: 4, children: [
),
Text('${availableBalanceLabel}',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 12,
fontFamily: 'Lato',
fontWeight: FontWeight.w400,
color: Theme.of(context)
.accentTextTheme!
.displaySmall!
.backgroundColor!,
height: 1)),
SizedBox(height: 5),
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ mainAxisAlignment: MainAxisAlignment.spaceBetween,
Expanded( crossAxisAlignment: CrossAxisAlignment.start,
child: AutoSizeText(availableBalance, children: [
GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () => _showBalanceDescription(context),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Text('${availableBalanceLabel}',
style: TextStyle(
fontSize: 12,
fontFamily: 'Lato',
fontWeight: FontWeight.w400,
color: Theme.of(context)
.accentTextTheme!
.displaySmall!
.backgroundColor!,
height: 1)),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 4),
child: Icon(Icons.help_outline,
size: 16,
color: Theme.of(context)
.accentTextTheme!
.displaySmall!
.backgroundColor!),
)
],
),SizedBox(
height: 6,
),
AutoSizeText(availableBalance,
style: TextStyle(
fontSize: 24,
fontFamily: 'Lato',
fontWeight: FontWeight.w900,
color: Theme.of(context)
.accentTextTheme!
.displayMedium!
.backgroundColor!,
height: 1),
maxLines: 1,
textAlign: TextAlign.start),
SizedBox(
height: 6,
),
Text('${availableFiatBalance}',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 16,
fontFamily: 'Lato',
fontWeight: FontWeight.w500,
color: Theme.of(context)
.accentTextTheme!
.displayMedium!
.backgroundColor!,
height: 1)),
],
),
),
Text(currency,
style: TextStyle( style: TextStyle(
fontSize: 24, fontSize: 28,
fontFamily: 'Lato', fontFamily: 'Lato',
fontWeight: FontWeight.w900, fontWeight: FontWeight.w800,
color: Theme.of(context) color: Theme.of(context)
.accentTextTheme! .accentTextTheme!
.displayMedium! .displayMedium!
.backgroundColor!, .backgroundColor!,
height: 1), height: 1)),
maxLines: 1, ],
textAlign: TextAlign.start),
),
Text(currency,
style: TextStyle(
fontSize: 28,
fontFamily: 'Lato',
fontWeight: FontWeight.w800,
color: Theme.of(context)
.accentTextTheme!
.displayMedium!
.backgroundColor!,
height: 1)),
]),
SizedBox(
height: 4,
), ),
Text('${availableFiatBalance}',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 16,
fontFamily: 'Lato',
fontWeight: FontWeight.w500,
color: Theme.of(context)
.accentTextTheme!
.displayMedium!
.backgroundColor!,
height: 1)),
SizedBox(height: 26), SizedBox(height: 26),
if (frozenBalance.isNotEmpty) if (frozenBalance.isNotEmpty)
Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
@ -247,4 +270,10 @@ class BalancePage extends StatelessWidget {
])), ])),
); );
} }
void _showBalanceDescription(BuildContext context) {
showPopUp<void>(
context: context,
builder: (_) => InformationPage(information: S.current.available_balance_description));
}
} }

View file

@ -1,7 +1,7 @@
import 'package:cake_wallet/routes.dart'; import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart'; import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cake_wallet/src/widgets/market_place_item.dart';
import 'package:cake_wallet/store/settings_store.dart'; import 'package:cake_wallet/store/settings_store.dart';
import 'package:cake_wallet/src/widgets/dashboard_card_widget.dart';
import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:cake_wallet/utils/show_pop_up.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:cake_wallet/view_model/dashboard/market_place_view_model.dart'; import 'package:cake_wallet/view_model/dashboard/market_place_view_model.dart';
@ -51,15 +51,17 @@ class MarketPlacePage extends StatelessWidget {
child: ListView( child: ListView(
controller: _scrollController, controller: _scrollController,
children: <Widget>[ children: <Widget>[
if (!SettingsStoreBase.walletPasswordDirectInput)
...[SizedBox(height: 20),
MarketPlaceItem(
onTap: () => _navigatorToGiftCardsPage(context),
title: S.of(context).cake_pay_title,
subTitle: S.of(context).cake_pay_subtitle,
)],
SizedBox(height: 20), SizedBox(height: 20),
MarketPlaceItem( DashBoardRoundedCardWidget(
onTap: () => launchUrl(
Uri.parse("https://cakelabs.com/news/cake-pay-mobile-to-shut-down/"),
mode: LaunchMode.externalApplication,
),
title: S.of(context).cake_pay_title,
subTitle: S.of(context).cake_pay_subtitle,
),
SizedBox(height: 20),
DashBoardRoundedCardWidget(
onTap: () => launchUrl( onTap: () => launchUrl(
Uri.https("buy.cakepay.com"), Uri.https("buy.cakepay.com"),
mode: LaunchMode.externalApplication, mode: LaunchMode.externalApplication,
@ -77,6 +79,7 @@ class MarketPlacePage extends StatelessWidget {
); );
} }
// TODO: Remove ionia flow/files if we will discard it
void _navigatorToGiftCardsPage(BuildContext context) { void _navigatorToGiftCardsPage(BuildContext context) {
final walletType = dashboardViewModel.type; final walletType = dashboardViewModel.type;

View file

@ -138,7 +138,7 @@ class PresentReceiveOptionPicker extends StatelessWidget {
Container( Container(
margin: EdgeInsets.only(bottom: 40), margin: EdgeInsets.only(bottom: 40),
child: InkWell( child: InkWell(
onTap: () => Navigator.pop(context), onTap: () => Navigator.pop(popUpContext),
child: CircleAvatar( child: CircleAvatar(
child: Icon( child: Icon(
Icons.close, Icons.close,

View file

@ -1,9 +1,11 @@
import 'package:cake_wallet/src/screens/dashboard/widgets/anonpay_transaction_row.dart'; import 'package:cake_wallet/src/screens/dashboard/widgets/anonpay_transaction_row.dart';
import 'package:cake_wallet/src/screens/dashboard/widgets/order_row.dart'; import 'package:cake_wallet/src/screens/dashboard/widgets/order_row.dart';
import 'package:cake_wallet/src/widgets/dashboard_card_widget.dart';
import 'package:cake_wallet/utils/responsive_layout_util.dart'; import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:cake_wallet/view_model/dashboard/anonpay_transaction_list_item.dart'; import 'package:cake_wallet/view_model/dashboard/anonpay_transaction_list_item.dart';
import 'package:cake_wallet/view_model/dashboard/order_list_item.dart'; import 'package:cake_wallet/view_model/dashboard/order_list_item.dart';
import 'package:cw_core/crypto_currency.dart'; import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/sync_status.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.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_mobx/flutter_mobx.dart'; import 'package:flutter_mobx/flutter_mobx.dart';
@ -37,6 +39,23 @@ class TransactionsPage extends StatelessWidget {
padding: EdgeInsets.only(top: 24, bottom: 24), padding: EdgeInsets.only(top: 24, bottom: 24),
child: Column( child: Column(
children: <Widget>[ children: <Widget>[
Observer(builder: (_) {
final status = dashboardViewModel.status;
if (status is SyncingSyncStatus) {
return Padding(
padding: const EdgeInsets.fromLTRB(24, 0, 24, 8),
child: DashBoardRoundedCardWidget(
onTap: () => Navigator.of(context).pushNamed(
Routes.webViewPage,
arguments: ['', Uri.parse('https://guides.cakewallet.com/docs/bugs-service-status/why_are_my_funds_not_appearing/')]),
title: S.of(context).syncing_wallet_alert_title,
subTitle: S.of(context).syncing_wallet_alert_content,
),
);
} else {
return Container();
}
}),
HeaderRow(dashboardViewModel: dashboardViewModel), HeaderRow(dashboardViewModel: dashboardViewModel),
Expanded(child: Observer(builder: (_) { Expanded(child: Observer(builder: (_) {
final items = dashboardViewModel.items; final items = dashboardViewModel.items;

View file

@ -126,7 +126,9 @@ class ExchangePage extends BasePage {
child: ButtonTheme( child: ButtonTheme(
minWidth: double.minPositive, minWidth: double.minPositive,
child: Semantics( child: Semantics(
label: !isMobileView ? 'Close' : 'Back', label: !isMobileView
? S.of(context).close
: S.of(context).seed_alert_back,
child: TextButton( child: TextButton(
style: ButtonStyle( style: ButtonStyle(
overlayColor: MaterialStateColor.resolveWith( overlayColor: MaterialStateColor.resolveWith(

View file

@ -359,7 +359,7 @@ class ExchangeTradeState extends State<ExchangeTradeForm> {
Navigator.of(popupContext).pop(); Navigator.of(popupContext).pop();
RequestReviewHandler.requestReview(); RequestReviewHandler.requestReview();
}, },
text: S.of(popupContext).send_got_it, text: S.of(popupContext).got_it,
color: Theme.of(popupContext) color: Theme.of(popupContext)
.accentTextTheme! .accentTextTheme!
.bodyLarge! .bodyLarge!

View file

@ -30,6 +30,7 @@ class InformationPage extends StatelessWidget {
padding: EdgeInsets.fromLTRB(24, 28, 24, 24), padding: EdgeInsets.fromLTRB(24, 28, 24, 24),
child: Text( child: Text(
information, information,
textAlign: TextAlign.center,
style: TextStyle( style: TextStyle(
fontSize: 14, fontSize: 14,
fontWeight: FontWeight.normal, fontWeight: FontWeight.normal,
@ -43,7 +44,7 @@ class InformationPage extends StatelessWidget {
padding: EdgeInsets.fromLTRB(10, 0, 10, 10), padding: EdgeInsets.fromLTRB(10, 0, 10, 10),
child: PrimaryButton( child: PrimaryButton(
onPressed: () => Navigator.of(context).pop(), onPressed: () => Navigator.of(context).pop(),
text: S.of(context).send_got_it, text: S.of(context).got_it,
color: Theme.of(context).accentTextTheme!.bodySmall!.backgroundColor!, color: Theme.of(context).accentTextTheme!.bodySmall!.backgroundColor!,
textColor: Theme.of(context).primaryTextTheme!.titleLarge!.color! textColor: Theme.of(context).primaryTextTheme!.titleLarge!.color!
), ),

View file

@ -283,7 +283,7 @@ class IoniaBuyGiftCardDetailPage extends BasePage {
}) })
.expand((e) => e) .expand((e) => e)
.toList()), .toList()),
actionTitle: S.current.send_got_it, actionTitle: S.current.got_it,
); );
}); });
} }

View file

@ -216,7 +216,7 @@ class IoniaDebitCardPage extends BasePage {
onPressed: () => activate onPressed: () => activate
? Navigator.pushNamed(context, Routes.ioniaActivateDebitCardPage) ? Navigator.pushNamed(context, Routes.ioniaActivateDebitCardPage)
: Navigator.pop(context), : Navigator.pop(context),
text: S.of(context).send_got_it, text: S.of(context).got_it,
color: Color.fromRGBO(233, 242, 252, 1), color: Color.fromRGBO(233, 242, 252, 1),
textColor: textColor:
Theme.of(context).textTheme!.displaySmall!.color!, Theme.of(context).textTheme!.displaySmall!.color!,

View file

@ -214,7 +214,7 @@ class IoniaGiftCardDetailPage extends BasePage {
}) })
.expand((e) => e) .expand((e) => e)
.toList()), .toList()),
actionTitle: S.of(context).send_got_it, actionTitle: S.of(context).got_it,
); );
}); });
} }

View file

@ -66,40 +66,6 @@ class IoniaManageCardsPage extends BasePage {
@override @override
Widget get endDrawer => CardMenu(); Widget get endDrawer => CardMenu();
@override
Widget leading(BuildContext context) {
final _backButton = Icon(
Icons.arrow_back_ios,
color: Theme.of(context)
.accentTextTheme!
.displayMedium!
.backgroundColor!,
size: 16,
);
return SizedBox(
height: 37,
width: 37,
child: ButtonTheme(
minWidth: double.minPositive,
child: TextButton(
// FIX-ME: Style
//highlightColor: Colors.transparent,
//splashColor: Colors.transparent,
//padding: EdgeInsets.all(0),
onPressed: (){
if (searchFocusNode.hasFocus) {
searchFocusNode.unfocus();
return;
}
Navigator.of(context).pop();
},
child: _backButton),
),
);
}
@override @override
Widget middle(BuildContext context) { Widget middle(BuildContext context) {
return Text( return Text(
@ -123,26 +89,28 @@ class IoniaManageCardsPage extends BasePage {
@override @override
Widget body(BuildContext context) { Widget body(BuildContext context) {
final filterButton = InkWell( final filterButton = Semantics(
onTap: () async { label: S.of(context).filter_by,
await showCategoryFilter(context); child: InkWell(
_cardsListViewModel.getMerchants(); onTap: () async {
}, await showCategoryFilter(context);
child: Container( _cardsListViewModel.getMerchants();
width: 32, },
padding: EdgeInsets.all(8), child: Container(
decoration: BoxDecoration( width: 32,
color: Theme.of(context).textTheme!.titleLarge!.backgroundColor!, padding: EdgeInsets.all(8),
border: Border.all( decoration: BoxDecoration(
color: Colors.white.withOpacity(0.2), color: Theme.of(context).textTheme!.titleLarge!.backgroundColor!,
border: Border.all(
color: Colors.white.withOpacity(0.2),
),
borderRadius: BorderRadius.circular(10),
), ),
borderRadius: BorderRadius.circular(10), child: Image.asset(
), 'assets/images/filter.png',
child: Image.asset( color: Theme.of(context).textTheme!.bodySmall!.decorationColor!,
'assets/images/filter.png', ),
color: Theme.of(context).textTheme!.bodySmall!.decorationColor!, )),
),
)
); );
return Padding( return Padding(
@ -281,11 +249,13 @@ class _SearchWidget extends StatelessWidget {
final FocusNode focusNode; final FocusNode focusNode;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final searchIcon = Padding( final searchIcon = ExcludeSemantics(
padding: EdgeInsets.all(8), child: Padding(
child: Image.asset( padding: EdgeInsets.all(8),
'assets/images/mini_search_icon.png', child: Image.asset(
color: Theme.of(context).textTheme!.bodySmall!.decorationColor!, 'assets/images/mini_search_icon.png',
color: Theme.of(context).textTheme!.bodySmall!.decorationColor!,
),
), ),
); );
@ -335,18 +305,22 @@ class _TrailingIcon extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Material( return Semantics(
color: Colors.transparent, label: S.of(context).profile,
child: IconButton( child: Material(
padding: EdgeInsets.zero, color: Colors.transparent,
constraints: BoxConstraints(), child: IconButton(
highlightColor: Colors.transparent, padding: EdgeInsets.zero,
splashColor: Colors.transparent, constraints: BoxConstraints(),
iconSize: 25, highlightColor: Colors.transparent,
onPressed: onPressed, splashColor: Colors.transparent,
icon: Image.asset( iconSize: 25,
asset, onPressed: onPressed,
color: Theme.of(context).accentTextTheme!.displayMedium!.backgroundColor!, icon: Image.asset(
asset,
color:
Theme.of(context).accentTextTheme!.displayMedium!.backgroundColor!,
),
), ),
), ),
); );

View file

@ -1,176 +1,94 @@
import 'package:cake_wallet/src/widgets/picker_inner_wrapper_widget.dart';
import 'package:cake_wallet/src/widgets/section_divider.dart'; import 'package:cake_wallet/src/widgets/section_divider.dart';
import 'package:cake_wallet/utils/responsive_layout_util.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';
import 'package:cake_wallet/routes.dart'; import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/view_model/monero_account_list/monero_account_list_view_model.dart'; import 'package:cake_wallet/view_model/monero_account_list/monero_account_list_view_model.dart';
import 'package:cake_wallet/src/screens/monero_accounts/widgets/account_tile.dart'; import 'package:cake_wallet/src/screens/monero_accounts/widgets/account_tile.dart';
import 'package:cake_wallet/src/widgets/alert_background.dart';
import 'package:cake_wallet/src/widgets/cake_scrollbar.dart';
import 'package:cake_wallet/src/widgets/alert_close_button.dart';
class MoneroAccountListPage extends StatelessWidget { class MoneroAccountListPage extends StatelessWidget {
MoneroAccountListPage({required this.accountListViewModel}) MoneroAccountListPage({required this.accountListViewModel});
: backgroundHeight = 194,
thumbHeight = 72,
isAlwaysShowScrollThumb = false,
controller = ScrollController() {
controller.addListener(() {
final scrollOffsetFromTop = controller.hasClients
? (controller.offset / controller.position.maxScrollExtent * (backgroundHeight - thumbHeight))
: 0.0;
accountListViewModel.setScrollOffsetFromTop(scrollOffsetFromTop);
});
}
final MoneroAccountListViewModel accountListViewModel; final MoneroAccountListViewModel accountListViewModel;
final ScrollController controller = ScrollController();
ScrollController controller;
double backgroundHeight;
double thumbHeight;
bool isAlwaysShowScrollThumb;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return AlertBackground( double itemHeight = 80;
child: Column( double buttonHeight = 62;
return Observer(builder: (_) {
final accounts = accountListViewModel.accounts;
return PickerInnerWrapperWidget(
title: S.of(context).choose_account,
itemsHeight: (itemHeight * accounts.length) + buttonHeight,
children: [ children: [
Expanded( Expanded(
child: Stack( child: Scrollbar(
alignment: Alignment.center, controller: controller,
children: <Widget>[ child: ListView.separated(
Column( padding: EdgeInsets.zero,
controller: controller,
separatorBuilder: (context, index) => const SectionDivider(),
itemCount: accounts.length,
itemBuilder: (context, index) {
final account = accounts[index];
return AccountTile(
isCurrent: account.isSelected,
accountName: account.label,
accountBalance: account.balance ?? '0.00',
currency: accountListViewModel.currency.toString(),
onTap: () {
if (account.isSelected) {
return;
}
accountListViewModel.select(account);
Navigator.of(context).pop();
},
onEdit: () async => await Navigator.of(context)
.pushNamed(Routes.accountCreation, arguments: account));
},
),
)),
GestureDetector(
onTap: () async =>
await Navigator.of(context).pushNamed(Routes.accountCreation),
child: Container(
height: buttonHeight,
color: Theme.of(context).cardColor,
padding: EdgeInsets.symmetric(horizontal: 24),
child: Center(
child: Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: <Widget>[ children: <Widget>[
Container( Icon(
padding: EdgeInsets.only(left: 24, right: 24), Icons.add,
child: Text( color: Colors.white,
S.of(context).choose_account,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
fontFamily: 'Lato',
decoration: TextDecoration.none,
color: Colors.white
),
),
), ),
Padding( Padding(
padding: EdgeInsets.only(left: 24, right: 24, top: 24), padding: EdgeInsets.only(left: 5),
child: GestureDetector( child: Text(
onTap: () => null, S.of(context).create_new_account,
child: ClipRRect( style: TextStyle(
borderRadius: BorderRadius.all(Radius.circular(14)), fontSize: 15,
child: Container( fontWeight: FontWeight.w600,
height: 296, fontFamily: 'Lato',
color: Theme.of(context).textTheme!.displayLarge!.decorationColor!, color: Colors.white,
child: Column( decoration: TextDecoration.none,
children: <Widget>[
Expanded(
child: Observer(
builder: (_) {
final accounts = accountListViewModel.accounts;
isAlwaysShowScrollThumb = accounts == null
? false
: accounts.length > 3;
return Stack(
alignment: Alignment.center,
children: <Widget>[
ListView.separated(
padding: EdgeInsets.zero,
controller: controller,
separatorBuilder: (context, index) =>
const SectionDivider(),
itemCount: accounts.length ?? 0,
itemBuilder: (context, index) {
final account = accounts[index];
return AccountTile(
isCurrent: account.isSelected,
accountName: account.label,
accountBalance: account.balance ?? '0.00',
currency: accountListViewModel
.currency.toString(),
onTap: () {
if (account.isSelected) {
return;
}
accountListViewModel
.select(account);
Navigator.of(context).pop();
},
onEdit: () async =>
await Navigator.of(context)
.pushNamed(
Routes.accountCreation,
arguments: account));
},
),
isAlwaysShowScrollThumb
? CakeScrollbar(
backgroundHeight: backgroundHeight,
thumbHeight: thumbHeight,
fromTop: accountListViewModel
.scrollOffsetFromTop
)
: Offstage(),
],
);
}
)
),
GestureDetector(
onTap: () async => await Navigator.of(context)
.pushNamed(Routes.accountCreation),
child: Container(
height: 62,
color: Theme.of(context).cardColor,
padding: EdgeInsets.only(left: 24, right: 24),
child: Center(
child: Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Icon(
Icons.add,
color: Colors.white,
),
Padding(
padding: EdgeInsets.only(left: 5),
child: Text(
S.of(context).create_new_account,
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w600,
fontFamily: 'Lato',
color: Colors.white,
decoration: TextDecoration.none,
),
),
)
],
),
),
),
)
],
),
),
), ),
), ),
) )
], ],
), ),
SizedBox(height: ResponsiveLayoutUtil.kPopupSpaceHeight), ),
AlertCloseButton()
],
), ),
), )
], ],
), );
); });
} }
} }

View file

@ -1,14 +1,13 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class AccountTile extends StatelessWidget { class AccountTile extends StatelessWidget {
AccountTile({ AccountTile(
required this.isCurrent, {required this.isCurrent,
required this.accountName, required this.accountName,
this.accountBalance, this.accountBalance,
required this.currency, required this.currency,
required this.onTap, required this.onTap,
required this.onEdit required this.onEdit});
});
final bool isCurrent; final bool isCurrent;
final String accountName; final String accountName;
@ -32,11 +31,13 @@ class AccountTile extends StatelessWidget {
height: 77, height: 77,
padding: EdgeInsets.only(left: 24, right: 24), padding: EdgeInsets.only(left: 24, right: 24),
color: color, color: color,
child: Row( child: Wrap(
mainAxisAlignment: MainAxisAlignment.spaceBetween, direction: Axis.horizontal,
alignment: WrapAlignment.spaceBetween,
runAlignment: WrapAlignment.center,
crossAxisAlignment: WrapCrossAlignment.center,
children: [ children: [
Expanded( Container(
flex: 2,
child: Text( child: Text(
accountName, accountName,
style: TextStyle( style: TextStyle(
@ -49,19 +50,19 @@ class AccountTile extends StatelessWidget {
), ),
), ),
if (accountBalance != null) if (accountBalance != null)
Expanded( Container(
child: Text( child: Text(
'${accountBalance.toString()} $currency', '${accountBalance.toString()} $currency',
textAlign: TextAlign.end, textAlign: TextAlign.end,
style: TextStyle( style: TextStyle(
fontSize: 15, fontSize: 15,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
fontFamily: 'Lato', fontFamily: 'Lato',
color: Theme.of(context).textTheme!.headlineMedium!.color!, color: Theme.of(context).textTheme!.headlineMedium!.color!,
decoration: TextDecoration.none, decoration: TextDecoration.none,
),
), ),
), ),
),
], ],
), ),
), ),

View file

@ -118,19 +118,21 @@ class _AdvancedPrivacySettingsBodyState extends State<AdvancedPrivacySettingsBod
textColor: Colors.white, textColor: Colors.white,
), ),
const SizedBox(height: 25), const SizedBox(height: 25),
Padding( LayoutBuilder(
padding: EdgeInsets.symmetric(horizontal: MediaQuery.of(context).size.width * 0.15), builder: (_, constraints) => SizedBox(
child: Text( width: constraints.maxWidth * 0.8,
S.of(context).settings_can_be_changed_later, child: Text(
textAlign: TextAlign.center, S.of(context).settings_can_be_changed_later,
style: TextStyle( textAlign: TextAlign.center,
color: Theme.of(context) style: TextStyle(
color: Theme.of(context)
.accentTextTheme! .accentTextTheme!
.displayMedium! .displayMedium!
.color, .color,
),
), ),
), ),
), )
], ],
), ),
), ),

View file

@ -149,7 +149,7 @@ class _WalletNameFormState extends State<WalletNameForm> {
width: 1.0), width: 1.0),
), ),
suffixIcon: Semantics( suffixIcon: Semantics(
label: 'Generate Name', label: S.of(context).generate_name,
child: IconButton( child: IconButton(
onPressed: () async { onPressed: () async {
final rName = await generateName(); final rName = await generateName();

View file

@ -1,13 +1,14 @@
import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:cake_wallet/themes/theme_base.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/screens/base_page.dart'; import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/screens/new_wallet/widgets/select_button.dart';
import 'package:cake_wallet/src/widgets/primary_button.dart'; import 'package:cake_wallet/src/widgets/primary_button.dart';
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart'; import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
import 'package:cake_wallet/src/screens/new_wallet/widgets/select_button.dart'; import 'package:cake_wallet/src/widgets/search_bar_widget.dart';
import 'package:cake_wallet/themes/theme_base.dart';
import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:cake_wallet/wallet_types.g.dart'; import 'package:cake_wallet/wallet_types.g.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:flutter/material.dart';
class NewWalletTypePage extends BasePage { class NewWalletTypePage extends BasePage {
NewWalletTypePage({required this.onTypeSelected}); NewWalletTypePage({required this.onTypeSelected});
@ -40,92 +41,82 @@ class WalletTypeFormState extends State<WalletTypeForm> {
static const aspectRatioImage = 1.22; static const aspectRatioImage = 1.22;
final moneroIcon = Image.asset('assets/images/monero_logo.png', height: 24, width: 24); final TextEditingController searchController = TextEditingController();
final bitcoinIcon = Image.asset('assets/images/bitcoin.png', height: 24, width: 24);
final litecoinIcon = Image.asset('assets/images/litecoin_icon.png', height: 24, width: 24);
final walletTypeImage = Image.asset('assets/images/wallet_type.png');
final walletTypeLightImage = Image.asset('assets/images/wallet_type_light.png');
final havenIcon = Image.asset('assets/images/haven_logo.png', height: 24, width: 24);
WalletType? selected; WalletType? selected;
List<WalletType> types; List<WalletType> types;
List<WalletType> filteredTypes = [];
@override @override
void initState() { void initState() {
types = availableWalletTypes; types = filteredTypes = availableWalletTypes;
super.initState(); super.initState();
searchController.addListener(() {
setState(() {
filteredTypes = List.from(types.where((type) => walletTypeToDisplayName(type)
.toLowerCase()
.contains(searchController.text.toLowerCase())));
return;
});
});
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return ScrollableWithBottomSection( return Center(
contentPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
content: Center(
child: ConstrainedBox( child: ConstrainedBox(
constraints: BoxConstraints(maxWidth: ResponsiveLayoutUtil.kDesktopMaxWidthConstraint), constraints: BoxConstraints(maxWidth: ResponsiveLayoutUtil.kDesktopMaxWidthConstraint),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.center, children: [
children: <Widget>[ Padding(
Padding( padding: EdgeInsets.only(top: 48),
padding: EdgeInsets.only(left: 12, right: 12), child: Text(
child: AspectRatio( S.of(context).choose_wallet_currency,
aspectRatio: aspectRatioImage, textAlign: TextAlign.center,
child: FittedBox(child: widget.walletImage, fit: BoxFit.fill)), style: TextStyle(
), fontSize: 16,
Padding( fontWeight: FontWeight.w500,
padding: EdgeInsets.only(top: 48), color: Theme.of(context).primaryTextTheme!.titleLarge!.color!),
child: Text( ),
S.of(context).choose_wallet_currency,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
color: Theme.of(context)
.primaryTextTheme!
.titleLarge!
.color!),
), ),
), Padding(
...types.map((type) => Padding( padding: const EdgeInsets.fromLTRB(24, 24, 24, 12),
padding: EdgeInsets.only(top: 24), child: SearchBarWidget(searchController: searchController, borderRadius: 24),
child: SelectButton( ),
image: _iconFor(type), Expanded(
text: walletTypeToDisplayName(type), child: ScrollableWithBottomSection(
isSelected: selected == type, contentPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
onTap: () => setState(() => selected = type)), content: Column(
)) crossAxisAlignment: CrossAxisAlignment.center,
], children: <Widget>[
), ...filteredTypes.map((type) => Padding(
), padding: EdgeInsets.only(top: 12),
), child: SelectButton(
bottomSectionPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24), image: Image.asset(
bottomSection: PrimaryButton( walletTypeToCryptoCurrency(type).iconPath ?? '',
onPressed: () => onTypeSelected(), height: 24,
text: S.of(context).seed_language_next, width: 24),
color: Theme.of(context) text: walletTypeToDisplayName(type),
.accentTextTheme! showTrailingIcon: false,
.bodyLarge! height: 54,
.color!, isSelected: selected == type,
textColor: Colors.white, onTap: () => setState(() => selected = type)),
isDisabled: selected == null, ))
), ],
); ),
} bottomSectionPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
bottomSection: PrimaryButton(
Image _iconFor(WalletType type) { onPressed: () => onTypeSelected(),
switch (type) { text: S.of(context).seed_language_next,
case WalletType.monero: color: Theme.of(context).accentTextTheme!.bodyLarge!.color!,
return moneroIcon; textColor: Colors.white,
case WalletType.bitcoin: isDisabled: selected == null,
return bitcoinIcon; ),
case WalletType.litecoin: ),
return litecoinIcon; ),
case WalletType.haven: ],
return havenIcon; )));
default:
throw Exception(
'_iconFor: Incorrect Wallet Type. Cannot find icon for Wallet Type: ${type.toString()}');
}
} }
Future<void> onTypeSelected() async { Future<void> onTypeSelected() async {

View file

@ -6,12 +6,16 @@ class SelectButton extends StatelessWidget {
required this.onTap, required this.onTap,
this.image, this.image,
this.isSelected = false, this.isSelected = false,
this.showTrailingIcon = true,
this.height = 60,
}); });
final Image? image; final Image? image;
final String text; final String text;
final bool isSelected; final bool isSelected;
final VoidCallback onTap; final VoidCallback onTap;
final bool showTrailingIcon;
final double height;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -44,7 +48,7 @@ class SelectButton extends StatelessWidget {
onTap: onTap, onTap: onTap,
child: Container( child: Container(
width: double.infinity, width: double.infinity,
height: 60, height: height,
padding: EdgeInsets.only(left: 30, right: 30), padding: EdgeInsets.only(left: 30, right: 30),
alignment: Alignment.center, alignment: Alignment.center,
decoration: BoxDecoration( decoration: BoxDecoration(
@ -76,7 +80,7 @@ class SelectButton extends StatelessWidget {
) )
], ],
), ),
selectArrowImage if (showTrailingIcon) selectArrowImage
], ],
), ),
), ),

View file

@ -1,6 +1,8 @@
import 'package:cake_wallet/core/execution_state.dart'; import 'package:cake_wallet/core/execution_state.dart';
import 'package:cake_wallet/palette.dart';
import 'package:cake_wallet/src/screens/nodes/widgets/node_form.dart'; import 'package:cake_wallet/src/screens/nodes/widgets/node_form.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart'; import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cw_core/node.dart'; import 'package:cw_core/node.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -115,23 +117,42 @@ class NodeCreateOrEditPage extends BasePage {
bottomSectionPadding: EdgeInsets.only(bottom: 24), bottomSectionPadding: EdgeInsets.only(bottom: 24),
bottomSection: Observer( bottomSection: Observer(
builder: (_) => Row( builder: (_) => Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[ children: <Widget>[
Flexible( Flexible(
child: Container( child: Container(
padding: EdgeInsets.only(right: 8.0), padding: EdgeInsets.only(right: 8.0),
child: LoadingPrimaryButton( child: LoadingPrimaryButton(
onPressed: () async { onPressed: () async {
if (_formKey.currentState != null && !_formKey.currentState!.validate()) { final confirmed = await showPopUp<bool>(
return; context: context,
} builder: (BuildContext context) {
return AlertWithTwoActions(
alertTitle:
S.of(context).remove_node,
alertContent: S
.of(context)
.remove_node_message,
rightButtonText:
S.of(context).remove,
leftButtonText:
S.of(context).cancel,
actionRightButton: () =>
Navigator.pop(context, true),
actionLeftButton: () =>
Navigator.pop(context, false));
}) ??
false;
await nodeCreateOrEditViewModel.connect(); if (confirmed) {
await editingNode!.delete();
Navigator.of(context).pop();
}
}, },
isLoading: nodeCreateOrEditViewModel text: S.of(context).delete,
.connectionState is IsExecutingState, isDisabled: !nodeCreateOrEditViewModel.isReady ||
text: S.of(context).node_test, (isSelected ?? false),
isDisabled: !nodeCreateOrEditViewModel.isReady, color: Palette.red,
color: Colors.orange,
textColor: Colors.white), textColor: Colors.white),
)), )),
Flexible( Flexible(

View file

@ -9,8 +9,8 @@ class NodeIndicator extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Container( return Container(
width: 8.0, width: 12.0,
height: 8.0, height: 12.0,
decoration: BoxDecoration( decoration: BoxDecoration(
shape: BoxShape.circle, color: isLive ? Palette.green : Palette.red), shape: BoxShape.circle, color: isLive ? Palette.green : Palette.red),
); );

View file

@ -1,21 +1,23 @@
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/nodes/widgets/node_indicator.dart'; import 'package:cake_wallet/src/screens/nodes/widgets/node_indicator.dart';
import 'package:cake_wallet/src/widgets/standard_list.dart'; import 'package:cake_wallet/src/widgets/standard_list.dart';
import 'package:cw_core/node.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class NodeListRow extends StandardListRow { class NodeListRow extends StandardListRow {
NodeListRow( NodeListRow(
{required String title, {required String title,
required this.node,
required void Function(BuildContext context) onTap, required void Function(BuildContext context) onTap,
required bool isSelected, required bool isSelected})
required this.isAlive})
: super(title: title, onTap: onTap, isSelected: isSelected); : super(title: title, onTap: onTap, isSelected: isSelected);
final Future<bool> isAlive; final Node node;
@override @override
Widget buildTrailing(BuildContext context) { Widget buildLeading(BuildContext context) {
return FutureBuilder( return FutureBuilder(
future: isAlive, future: node.requestNode(),
builder: (context, snapshot) { builder: (context, snapshot) {
switch (snapshot.connectionState) { switch (snapshot.connectionState) {
case ConnectionState.done: case ConnectionState.done:
@ -25,6 +27,24 @@ class NodeListRow extends StandardListRow {
} }
}); });
} }
@override
Widget buildTrailing(BuildContext context) {
return GestureDetector(
onTap: () => Navigator.of(context).pushNamed(Routes.newNode,
arguments: {'editingNode': node, 'isSelected': isSelected}),
child: Container(
padding: EdgeInsets.all(10),
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Theme.of(context)
.textTheme
.headlineMedium!
.decorationColor!),
child: Icon(Icons.edit,
size: 14,
color: Theme.of(context).textTheme.headlineMedium!.color!)));
}
} }
class NodeHeaderListRow extends StandardListRow { class NodeHeaderListRow extends StandardListRow {

View file

@ -226,7 +226,7 @@ class PinCodeState<T extends PinCodeWidget> extends State<T> {
child: Container( child: Container(
margin: EdgeInsets.only(left: marginLeft, right: marginRight), margin: EdgeInsets.only(left: marginLeft, right: marginRight),
child: Semantics( child: Semantics(
label: 'Delete', label: S.of(context).delete,
button: true, button: true,
onTap: () => _pop(), onTap: () => _pop(),
child: TextButton( child: TextButton(

View file

@ -8,7 +8,6 @@ import 'package:cake_wallet/src/screens/dashboard/widgets/present_receive_option
import 'package:cake_wallet/src/screens/receive/widgets/anonpay_input_form.dart'; import 'package:cake_wallet/src/screens/receive/widgets/anonpay_input_form.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart'; import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cake_wallet/src/widgets/keyboard_done_button.dart'; import 'package:cake_wallet/src/widgets/keyboard_done_button.dart';
import 'package:cake_wallet/utils/device_info.dart';
import 'package:cake_wallet/utils/responsive_layout_util.dart'; import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:cake_wallet/view_model/anon_invoice_page_view_model.dart'; import 'package:cake_wallet/view_model/anon_invoice_page_view_model.dart';
import 'package:cake_wallet/view_model/dashboard/receive_option_view_model.dart'; import 'package:cake_wallet/view_model/dashboard/receive_option_view_model.dart';
@ -54,9 +53,7 @@ class AnonPayInvoicePage extends BasePage {
AppBarStyle get appBarStyle => AppBarStyle.transparent; AppBarStyle get appBarStyle => AppBarStyle.transparent;
@override @override
void onClose(BuildContext context) { void onClose(BuildContext context) => Navigator.popUntil(context, (route) => route.isFirst);
Navigator.popUntil(context, ModalRoute.withName(Routes.dashboard));
}
@override @override
Widget middle(BuildContext context) => Widget middle(BuildContext context) =>

View file

@ -32,28 +32,7 @@ class AnonPayReceivePage extends BasePage {
bool get resizeToAvoidBottomInset => false; bool get resizeToAvoidBottomInset => false;
@override @override
Widget leading(BuildContext context) { void onClose(BuildContext context) => Navigator.popUntil(context, (route) => route.isFirst);
final _backButton = Icon(
Icons.arrow_back_ios,
color: Theme.of(context)
.accentTextTheme!
.displayMedium!
.backgroundColor!,
size: 16,
);
return SizedBox(
height: 37,
width: 37,
child: ButtonTheme(
minWidth: double.minPositive,
child: TextButton(
onPressed: () =>
Navigator.pushNamedAndRemoveUntil(context, Routes.dashboard, (route) => false),
child: _backButton),
),
);
}
@override @override
Widget middle(BuildContext context) { Widget middle(BuildContext context) {

View file

@ -87,7 +87,7 @@ class ReceivePage extends BasePage {
return Material( return Material(
color: Colors.transparent, color: Colors.transparent,
child: Semantics( child: Semantics(
label: 'Share', label: S.of(context).share,
child: IconButton( child: IconButton(
padding: EdgeInsets.zero, padding: EdgeInsets.zero,
constraints: BoxConstraints(), constraints: BoxConstraints(),

View file

@ -71,7 +71,7 @@ class AddressCell extends StatelessWidget {
), ),
)); ));
return Semantics( return Semantics(
label: 'Slidable', label: S.of(context).slidable,
selected: isCurrent, selected: isCurrent,
enabled: !isCurrent, enabled: !isCurrent,
child: Slidable( child: Slidable(

View file

@ -2,7 +2,6 @@ import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:cw_core/wallet_type.dart'; import 'package:cw_core/wallet_type.dart';
import 'package:cake_wallet/routes.dart'; import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/themes/theme_base.dart'; import 'package:cake_wallet/themes/theme_base.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/widgets/primary_button.dart'; import 'package:cake_wallet/src/widgets/primary_button.dart';
@ -37,46 +36,33 @@ class PreSeedPage extends BasePage {
alignment: Alignment.center, alignment: Alignment.center,
padding: EdgeInsets.all(24), padding: EdgeInsets.all(24),
child: ConstrainedBox( child: ConstrainedBox(
constraints: BoxConstraints(maxWidth: ResponsiveLayoutUtil.kDesktopMaxWidthConstraint), constraints: BoxConstraints(maxWidth: ResponsiveLayoutUtil.kDesktopMaxWidthConstraint),
child: Column( child: Column(
children: [ mainAxisAlignment: MainAxisAlignment.spaceBetween,
Flexible( children: <Widget>[
flex: 2, ConstrainedBox(
child: AspectRatio( constraints: BoxConstraints(
aspectRatio: 1, maxHeight: MediaQuery.of(context).size.height * 0.3
child: FittedBox(child: image, fit: BoxFit.contain))), ),
Flexible( child: AspectRatio(aspectRatio: 1, child: image),
flex: 3, ),
child: Column( Padding(
mainAxisAlignment: MainAxisAlignment.spaceBetween, padding: EdgeInsets.all(10),
children: [ child: Text(
Padding( S.of(context).pre_seed_description(wordsCount.toString()),
padding: EdgeInsets.only(top: 70, left: 16, right: 16), textAlign: TextAlign.center,
child: Text( style: TextStyle(
S fontSize: 14,
.of(context) fontWeight: FontWeight.normal,
.pre_seed_description(wordsCount.toString()), color: Theme.of(context).primaryTextTheme.bodySmall!.color!),
textAlign: TextAlign.center, ),
style: TextStyle( ),
fontSize: 14, PrimaryButton(
fontWeight: FontWeight.normal, onPressed: () =>
color: Theme.of(context) Navigator.of(context).popAndPushNamed(Routes.seed, arguments: true),
.primaryTextTheme! text: S.of(context).pre_seed_button_text,
.bodySmall! color: Theme.of(context).accentTextTheme!.bodyLarge!.color!,
.color!), textColor: Colors.white)
),
),
PrimaryButton(
onPressed: () => Navigator.of(context)
.popAndPushNamed(Routes.seed, arguments: true),
text: S.of(context).pre_seed_button_text,
color: Theme.of(context)
.accentTextTheme!
.bodyLarge!
.color!,
textColor: Colors.white)
],
))
], ],
), ),
), ),

View file

@ -52,8 +52,7 @@ class WalletSeedPage extends BasePage {
} }
@override @override
Widget? leading(BuildContext context) => Widget? leading(BuildContext context) => isNewWalletCreated ? null : super.leading(context);
isNewWalletCreated ? null: super.leading(context);
@override @override
Widget trailing(BuildContext context) { Widget trailing(BuildContext context) {
@ -67,16 +66,11 @@ class WalletSeedPage extends BasePage {
margin: EdgeInsets.only(left: 10), margin: EdgeInsets.only(left: 10),
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(16)), borderRadius: BorderRadius.all(Radius.circular(16)),
color: Theme.of(context) color: Theme.of(context).accentTextTheme.bodySmall!.color!),
.accentTextTheme!
.bodySmall!
.color!),
child: Text( child: Text(
S.of(context).seed_language_next, S.of(context).seed_language_next,
style: TextStyle( style: TextStyle(
fontSize: 14, fontSize: 14, fontWeight: FontWeight.w600, color: Palette.blueCraiola),
fontWeight: FontWeight.w600,
color: Palette.blueCraiola),
), ),
), ),
) )
@ -87,121 +81,99 @@ class WalletSeedPage extends BasePage {
Widget body(BuildContext context) { Widget body(BuildContext context) {
final image = currentTheme.type == ThemeType.dark ? imageDark : imageLight; final image = currentTheme.type == ThemeType.dark ? imageDark : imageLight;
return WillPopScope(onWillPop: () async => false, child: Container( return WillPopScope(
padding: EdgeInsets.all(24), onWillPop: () async => false,
alignment: Alignment.center, child: Container(
child: ConstrainedBox( padding: EdgeInsets.all(24),
constraints: alignment: Alignment.center,
BoxConstraints(maxWidth: ResponsiveLayoutUtil.kDesktopMaxWidthConstraint), child: ConstrainedBox(
child: Column( constraints: BoxConstraints(maxWidth: ResponsiveLayoutUtil.kDesktopMaxWidthConstraint),
children: <Widget>[ child: Column(
Flexible( mainAxisAlignment: MainAxisAlignment.spaceBetween,
flex: 2, children: <Widget>[
child: AspectRatio( ConstrainedBox(
aspectRatio: 1, constraints:
child: FittedBox(child: image, fit: BoxFit.fill))), BoxConstraints(maxHeight: MediaQuery.of(context).size.height * 0.3),
Flexible( child: AspectRatio(aspectRatio: 1, child: image),
flex: 3, ),
child: Column( Observer(builder: (_) {
mainAxisAlignment: MainAxisAlignment.spaceBetween, return Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[ children: <Widget>[
Padding( Text(
padding: EdgeInsets.only(top: 33), walletSeedViewModel.name,
child: Observer(builder: (_) { style: TextStyle(
return Column( fontSize: 20,
crossAxisAlignment: CrossAxisAlignment.center, fontWeight: FontWeight.w600,
children: <Widget>[ color: Theme.of(context).primaryTextTheme.titleLarge!.color!),
Text(
walletSeedViewModel.name,
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w600,
color: Theme.of(context)
.primaryTextTheme!
.titleLarge!
.color!),
),
Padding(
padding:
EdgeInsets.only(top: 20, left: 16, right: 16),
child: Text(
walletSeedViewModel.seed,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.normal,
color: Theme.of(context)
.primaryTextTheme!
.bodySmall!
.color!),
),
)
],
);
}),
), ),
Column( Padding(
children: <Widget>[ padding: EdgeInsets.only(top: 20, left: 16, right: 16),
isNewWalletCreated child: Text(
? Padding( walletSeedViewModel.seed,
padding: EdgeInsets.only( textAlign: TextAlign.center,
bottom: 52, left: 43, right: 43), style: TextStyle(
child: Text( fontSize: 14,
S.of(context).seed_reminder, fontWeight: FontWeight.normal,
textAlign: TextAlign.center, color: Theme.of(context).primaryTextTheme.bodySmall!.color!),
style: TextStyle( ),
fontSize: 12,
fontWeight: FontWeight.normal,
color: Theme.of(context)
.primaryTextTheme!
.labelSmall!
.color!),
),
)
: Offstage(),
Row(
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Flexible(
child: Container(
padding: EdgeInsets.only(right: 8.0),
child: PrimaryButton(
onPressed: () {
ShareUtil.share(
text: walletSeedViewModel.seed,
context: context,
);
},
text: S.of(context).save,
color: Colors.green,
textColor: Colors.white),
)),
Flexible(
child: Container(
padding: EdgeInsets.only(left: 8.0),
child: Builder(
builder: (context) => PrimaryButton(
onPressed: () {
Clipboard.setData(ClipboardData(
text: walletSeedViewModel.seed));
showBar<void>(context,
S.of(context).copied_to_clipboard);
},
text: S.of(context).copy,
color: Theme.of(context)
.accentTextTheme!
.bodyMedium!
.color!,
textColor: Colors.white)),
))
],
)
],
) )
], ],
)) );
], }),
Column(
children: <Widget>[
isNewWalletCreated
? Padding(
padding: EdgeInsets.only(bottom: 43, left: 43, right: 43),
child: Text(
S.of(context).seed_reminder,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.normal,
color: Theme.of(context).primaryTextTheme.labelSmall!.color!),
),
)
: Offstage(),
Row(
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Flexible(
child: Container(
padding: EdgeInsets.only(right: 8.0),
child: PrimaryButton(
onPressed: () {
ShareUtil.share(
text: walletSeedViewModel.seed,
context: context,
);
},
text: S.of(context).save,
color: Colors.green,
textColor: Colors.white),
)),
Flexible(
child: Container(
padding: EdgeInsets.only(left: 8.0),
child: Builder(
builder: (context) => PrimaryButton(
onPressed: () {
Clipboard.setData(
ClipboardData(text: walletSeedViewModel.seed));
showBar<void>(context, S.of(context).copied_to_clipboard);
},
text: S.of(context).copy,
color: Theme.of(context).accentTextTheme.bodyMedium!.color!,
textColor: Colors.white)),
))
],
)
],
)
],
),
), ),
))); ));
} }
} }

View file

@ -71,7 +71,9 @@ class SendPage extends BasePage {
child: ButtonTheme( child: ButtonTheme(
minWidth: double.minPositive, minWidth: double.minPositive,
child: Semantics( child: Semantics(
label: !isMobileView ? 'Close' : 'Back', label: !isMobileView
? S.of(context).close
: S.of(context).seed_alert_back,
child: TextButton( child: TextButton(
style: ButtonStyle( style: ButtonStyle(
overlayColor: MaterialStateColor.resolveWith( overlayColor: MaterialStateColor.resolveWith(

View file

@ -12,7 +12,6 @@ import 'package:cake_wallet/src/screens/nodes/widgets/node_list_row.dart';
import 'package:cake_wallet/src/widgets/standard_list.dart'; import 'package:cake_wallet/src/widgets/standard_list.dart';
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart'; import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
import 'package:cake_wallet/view_model/node_list/node_list_view_model.dart'; import 'package:cake_wallet/view_model/node_list/node_list_view_model.dart';
import 'package:flutter_slidable/flutter_slidable.dart';
class ConnectionSyncPage extends BasePage { class ConnectionSyncPage extends BasePage {
ConnectionSyncPage(this.nodeListViewModel, this.dashboardViewModel); ConnectionSyncPage(this.nodeListViewModel, this.dashboardViewModel);
@ -64,49 +63,37 @@ class ConnectionSyncPage extends BasePage {
itemBuilder: (_, sectionIndex, index) { itemBuilder: (_, sectionIndex, index) {
final node = nodeListViewModel.nodes[index]; final node = nodeListViewModel.nodes[index];
final isSelected = node.keyIndex == nodeListViewModel.currentNode.keyIndex; final isSelected = node.keyIndex == nodeListViewModel.currentNode.keyIndex;
final nodeListRow = Semantics( final nodeListRow = NodeListRow(
label: 'Slidable', title: node.uriRaw,
selected: isSelected, node: node,
enabled: !isSelected, isSelected: isSelected,
child: NodeListRow( onTap: (_) async {
title: node.uriRaw, if (isSelected) {
isSelected: isSelected, return;
isAlive: node.requestNode(), }
onTap: (_) async {
if (isSelected) {
return;
}
await showPopUp<void>( await showPopUp<void>(
context: context, context: context,
builder: (BuildContext context) { builder: (BuildContext context) {
return AlertWithTwoActions( return AlertWithTwoActions(
alertTitle: alertTitle:
S.of(context).change_current_node_title, S.of(context).change_current_node_title,
alertContent: nodeListViewModel alertContent: nodeListViewModel
.getAlertContent(node.uriRaw), .getAlertContent(node.uriRaw),
leftButtonText: S.of(context).cancel, leftButtonText: S.of(context).cancel,
rightButtonText: S.of(context).change, rightButtonText: S.of(context).change,
actionLeftButton: () => actionLeftButton: () =>
Navigator.of(context).pop(), Navigator.of(context).pop(),
actionRightButton: () async { actionRightButton: () async {
await nodeListViewModel.setAsCurrent(node); await nodeListViewModel.setAsCurrent(node);
Navigator.of(context).pop(); Navigator.of(context).pop();
}, },
); );
}); });
}, },
),
); );
final dismissibleRow = Slidable( return nodeListRow;
key: Key('${node.keyIndex}'),
startActionPane: _actionPane(context, node, isSelected),
endActionPane: _actionPane(context, node, isSelected),
child: nodeListRow,
);
return dismissibleRow;
}, },
), ),
); );
@ -134,44 +121,4 @@ class ConnectionSyncPage extends BasePage {
}, },
); );
} }
ActionPane _actionPane(BuildContext context, Node node, bool isSelected) => ActionPane(
motion: const ScrollMotion(),
extentRatio: isSelected ? 0.3 : 0.6,
children: [
if (!isSelected)
SlidableAction(
onPressed: (context) async {
final confirmed = await showPopUp<bool>(
context: context,
builder: (BuildContext context) {
return AlertWithTwoActions(
alertTitle: S.of(context).remove_node,
alertContent: S.of(context).remove_node_message,
rightButtonText: S.of(context).remove,
leftButtonText: S.of(context).cancel,
actionRightButton: () => Navigator.pop(context, true),
actionLeftButton: () => Navigator.pop(context, false));
}) ??
false;
if (confirmed) {
await nodeListViewModel.delete(node);
}
},
backgroundColor: Colors.red,
foregroundColor: Colors.white,
icon: CupertinoIcons.delete,
label: S.of(context).delete,
),
SlidableAction(
onPressed: (_) => Navigator.of(context).pushNamed(Routes.newNode,
arguments: {'editingNode': node, 'isSelected': isSelected}),
backgroundColor: Colors.blue,
foregroundColor: Colors.white,
icon: Icons.edit,
label: S.of(context).edit,
),
],
);
} }

View file

@ -0,0 +1,175 @@
import 'package:another_flushbar/flushbar.dart';
import 'package:cake_wallet/core/auth_service.dart';
import 'package:cake_wallet/core/wallet_name_validator.dart';
import 'package:cake_wallet/palette.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
import 'package:cake_wallet/utils/show_bar.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/wallet_list/wallet_edit_view_model.dart';
import 'package:cake_wallet/view_model/wallet_list/wallet_list_item.dart';
import 'package:cake_wallet/view_model/wallet_new_vm.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/widgets/primary_button.dart';
import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
class WalletEditPage extends BasePage {
WalletEditPage(
{required this.walletEditViewModel,
required this.editingWallet,
required this.walletNewVM,
required this.authService})
: _formKey = GlobalKey<FormState>(),
_labelController = TextEditingController(),
super() {
_labelController.text = editingWallet.name;
_labelController.addListener(() => walletEditViewModel.newName = _labelController.text);
}
final GlobalKey<FormState> _formKey;
final TextEditingController _labelController;
final WalletEditViewModel walletEditViewModel;
final WalletNewVM walletNewVM;
final WalletListItem editingWallet;
final AuthService authService;
@override
String get title => S.current.wallet_list_edit_wallet;
Flushbar<void>? _progressBar;
@override
Widget body(BuildContext context) {
return Form(
key: _formKey,
child: Container(
padding: EdgeInsets.all(24.0),
child: Column(
children: <Widget>[
Expanded(
child: Center(
child: BaseTextFormField(
controller: _labelController,
hintText: S.of(context).wallet_list_wallet_name,
validator: WalletNameValidator()))),
Observer(
builder: (_) {
final isLoading = walletEditViewModel.state is WalletEditRenamePending ||
walletEditViewModel.state is WalletEditDeletePending;
return Row(
children: <Widget>[
Flexible(
child: Container(
padding: EdgeInsets.only(right: 8.0),
child: LoadingPrimaryButton(
isDisabled: isLoading,
onPressed: () => _removeWallet(context),
text: S.of(context).delete,
color: Palette.red,
textColor: Colors.white),
),
),
Flexible(
child: Container(
padding: EdgeInsets.only(left: 8.0),
child: LoadingPrimaryButton(
onPressed: () async {
if (_formKey.currentState?.validate() ?? false) {
if (walletNewVM.nameExists(walletEditViewModel.newName)) {
showPopUp<void>(
context: context,
builder: (_) {
return AlertWithOneAction(
alertTitle: '',
alertContent: S.of(context).wallet_name_exists,
buttonText: S.of(context).ok,
buttonAction: () => Navigator.of(context).pop(),
);
},
);
} else {
try {
await walletEditViewModel.changeName(editingWallet);
Navigator.of(context).pop();
walletEditViewModel.resetState();
} catch (e) {}
}
}
},
text: S.of(context).save,
color: Theme.of(context).accentTextTheme.bodyLarge!.color!,
textColor: Colors.white,
isDisabled: walletEditViewModel.newName.isEmpty || isLoading,
),
),
)
],
);
},
)
],
),
),
);
}
Future<void> _removeWallet(BuildContext context) async {
authService.authenticateAction(context, onAuthSuccess: (isAuthenticatedSuccessfully) async {
if (!isAuthenticatedSuccessfully) {
return;
}
_onSuccessfulAuth(context);
});
}
void _onSuccessfulAuth(BuildContext context) async {
bool confirmed = false;
await showPopUp<void>(
context: context,
builder: (BuildContext dialogContext) {
return AlertWithTwoActions(
alertTitle: S.of(context).delete_wallet,
alertContent: S.of(context).delete_wallet_confirm_message(editingWallet.name),
leftButtonText: S.of(context).cancel,
rightButtonText: S.of(context).delete,
actionLeftButton: () => Navigator.of(dialogContext).pop(),
actionRightButton: () {
confirmed = true;
Navigator.of(dialogContext).pop();
});
});
if (confirmed) {
Navigator.of(context).pop();
try {
changeProcessText(context, S.of(context).wallet_list_removing_wallet(editingWallet.name));
await walletEditViewModel.remove(editingWallet);
hideProgressText();
} catch (e) {
changeProcessText(
context,
S.of(context).wallet_list_failed_to_remove(editingWallet.name, e.toString()),
);
}
}
}
void changeProcessText(BuildContext context, String text) {
_progressBar = createBar<void>(text, duration: null)..show(context);
}
Future<void> hideProgressText() async {
await Future.delayed(Duration(milliseconds: 50), () {
_progressBar?.dismiss();
_progressBar = null;
});
}
}

View file

@ -8,7 +8,6 @@ import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/wallet_list/wallet_list_item.dart'; import 'package:cake_wallet/view_model/wallet_list/wallet_list_item.dart';
import 'package:another_flushbar/flushbar.dart'; import 'package:another_flushbar/flushbar.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:cake_wallet/routes.dart'; import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/generated/i18n.dart';
@ -17,7 +16,6 @@ import 'package:cake_wallet/view_model/wallet_list/wallet_list_view_model.dart';
import 'package:cake_wallet/src/widgets/primary_button.dart'; import 'package:cake_wallet/src/widgets/primary_button.dart';
import 'package:cake_wallet/src/screens/base_page.dart'; import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart'; import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
import 'package:flutter_slidable/flutter_slidable.dart';
import 'package:cake_wallet/wallet_type_utils.dart'; import 'package:cake_wallet/wallet_type_utils.dart';
class WalletListPage extends BasePage { class WalletListPage extends BasePage {
@ -79,31 +77,7 @@ class WalletListBodyState extends State<WalletListBody> {
? Theme.of(context).accentTextTheme!.titleSmall!.decorationColor! ? Theme.of(context).accentTextTheme!.titleSmall!.decorationColor!
: Theme.of(context).colorScheme.background; : Theme.of(context).colorScheme.background;
final row = GestureDetector( final row = GestureDetector(
onTap: () async { onTap: () => wallet.isCurrent ? null : _loadWallet(wallet),
if (wallet.isCurrent || !wallet.isEnabled) {
return;
}
final confirmed = await showPopUp<bool>(
context: context,
builder: (dialogContext) {
return AlertWithTwoActions(
alertTitle: S.of(context).change_wallet_alert_title,
alertContent:
S.of(context).change_wallet_alert_content(wallet.name),
leftButtonText: S.of(context).cancel,
rightButtonText: S.of(context).change,
actionLeftButton: () =>
Navigator.of(dialogContext).pop(false),
actionRightButton: () =>
Navigator.of(dialogContext).pop(true));
}) ??
false;
if (confirmed) {
await _loadWallet(wallet);
}
},
child: Container( child: Container(
height: tileHeight, height: tileHeight,
width: double.infinity, width: double.infinity,
@ -131,16 +105,21 @@ class WalletListBodyState extends State<WalletListBody> {
? _imageFor(type: wallet.type) ? _imageFor(type: wallet.type)
: nonWalletTypeIcon, : nonWalletTypeIcon,
SizedBox(width: 10), SizedBox(width: 10),
Text( Flexible(
wallet.name, child: Text(
style: TextStyle( wallet.name,
maxLines: null,
softWrap: true,
style: TextStyle(
fontSize: 22, fontSize: 22,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
color: Theme.of(context) color: Theme.of(context)
.primaryTextTheme .primaryTextTheme
.titleLarge! .titleLarge!
.color!), .color!,
) ),
),
),
], ],
), ),
), ),
@ -151,12 +130,38 @@ class WalletListBodyState extends State<WalletListBody> {
return wallet.isCurrent return wallet.isCurrent
? row ? row
: Slidable( : Row(children: [
key: Key('${wallet.key}'), Expanded(child: row),
startActionPane: _actionPane(wallet), GestureDetector(
endActionPane: _actionPane(wallet), onTap: () => Navigator.of(context).pushNamed(
child: row, Routes.walletEdit,
); arguments: [widget.walletListViewModel, wallet]),
child: Container(
padding: EdgeInsets.only(right: 20),
child: Center(
child: Container(
height: 40,
width: 44,
padding: EdgeInsets.all(10),
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Theme.of(context)
.textTheme
.headlineMedium!
.decorationColor!),
child: Icon(
Icons.edit,
size: 14,
color: Theme.of(context)
.textTheme
.headlineMedium!
.color!,
),
),
),
),
)
]);
}), }),
), ),
), ),
@ -217,7 +222,7 @@ class WalletListBodyState extends State<WalletListBody> {
await hideProgressText(); await hideProgressText();
// only pop the wallets route in mobile as it will go back to dashboard page // only pop the wallets route in mobile as it will go back to dashboard page
// in desktop platforms the navigation tree is different // in desktop platforms the navigation tree is different
if (ResponsiveLayoutUtil.instance.isMobile) { if (ResponsiveLayoutUtil.instance.shouldRenderMobileUI()) {
WidgetsBinding.instance.addPostFrameCallback((_) { WidgetsBinding.instance.addPostFrameCallback((_) {
Navigator.of(context).pop(); Navigator.of(context).pop();
}); });
@ -228,47 +233,6 @@ class WalletListBodyState extends State<WalletListBody> {
}); });
} }
Future<void> _removeWallet(WalletListItem wallet) async {
widget.authService.authenticateAction(context,
onAuthSuccess: (isAuthenticatedSuccessfully) async {
if (!isAuthenticatedSuccessfully) {
return;
}
_onSuccessfulAuth(wallet);
});
}
void _onSuccessfulAuth(WalletListItem wallet) async {
bool confirmed = false;
await showPopUp<void>(
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 {
changeProcessText(S.of(context).wallet_list_removing_wallet(wallet.name));
await widget.walletListViewModel.remove(wallet);
hideProgressText();
} catch (e) {
changeProcessText(
S.of(context).wallet_list_failed_to_remove(wallet.name, e.toString()),
);
}
}
}
void changeProcessText(String text) { void changeProcessText(String text) {
_progressBar = createBar<void>(text, duration: null)..show(context); _progressBar = createBar<void>(text, duration: null)..show(context);
} }
@ -279,18 +243,4 @@ class WalletListBodyState extends State<WalletListBody> {
_progressBar = null; _progressBar = null;
}); });
} }
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,
),
],
);
} }

View file

@ -46,145 +46,133 @@ class WelcomePage extends BasePage {
@override @override
Widget body(BuildContext context) { Widget body(BuildContext context) {
final welcomeImage = currentTheme.type == ThemeType.dark ? welcomeImageDark : welcomeImageLight; final welcomeImage = currentTheme.type == ThemeType.dark
? welcomeImageDark
: welcomeImageLight;
final newWalletImage = Image.asset('assets/images/new_wallet.png', final newWalletImage = Image.asset('assets/images/new_wallet.png',
height: 12, height: 12,
width: 12, width: 12,
color: Theme.of(context) color: Theme.of(context)
.accentTextTheme! .accentTextTheme.headlineSmall!
.headlineSmall!
.decorationColor!); .decorationColor!);
final restoreWalletImage = Image.asset('assets/images/restore_wallet.png', final restoreWalletImage = Image.asset('assets/images/restore_wallet.png',
height: 12, height: 12,
width: 12, width: 12,
color: Theme.of(context).primaryTextTheme!.titleLarge!.color!);
color: Theme.of(context).primaryTextTheme.titleLarge!.color!);
return WillPopScope( return WillPopScope(
onWillPop: () async => false, onWillPop: () async => false,
child: Container( child: Container(
alignment: Alignment.center,
padding: EdgeInsets.only(top: 64, bottom: 24, left: 24, right: 24), padding: EdgeInsets.only(top: 64, bottom: 24, left: 24, right: 24),
child: Center( child: ConstrainedBox(
child: ConstrainedBox( constraints: BoxConstraints(
constraints: maxWidth: ResponsiveLayoutUtil.kDesktopMaxWidthConstraint),
BoxConstraints(maxWidth: ResponsiveLayoutUtil.kDesktopMaxWidthConstraint), child: Column(
child: Column( mainAxisAlignment: MainAxisAlignment.spaceBetween,
mainAxisAlignment: MainAxisAlignment.spaceBetween, children: <Widget>[
children: <Widget>[ Column(
Flexible( children: <Widget>[
flex: 2, AspectRatio(
child: AspectRatio( aspectRatio: aspectRatioImage,
aspectRatio: aspectRatioImage, child: FittedBox(
child: FittedBox(child: welcomeImage, fit: BoxFit.fill))), child: welcomeImage, fit: BoxFit.contain),
Flexible( ),
flex: 3, Padding(
child: Column( padding: EdgeInsets.only(top: 24),
mainAxisAlignment: MainAxisAlignment.spaceBetween, child: Text(
children: <Widget>[ S.of(context).welcome,
Column( style: TextStyle(
children: <Widget>[ fontSize: 18,
Padding( fontWeight: FontWeight.w500,
padding: EdgeInsets.only(top: 24), color: Theme.of(context)
child: Text( .accentTextTheme.displayMedium!
S.of(context).welcome, .color!,
style: TextStyle( ),
fontSize: 18, textAlign: TextAlign.center,
fontWeight: FontWeight.w500, ),
color: Theme.of(context) ),
.accentTextTheme! Padding(
.displayMedium! padding: EdgeInsets.only(top: 5),
.color, child: Text(
), appTitle(context),
textAlign: TextAlign.center, style: TextStyle(
), fontSize: 36,
), fontWeight: FontWeight.bold,
Padding( color: Theme.of(context)
padding: EdgeInsets.only(top: 5), .primaryTextTheme.titleLarge!
child: Text( .color!,
appTitle(context), ),
style: TextStyle( textAlign: TextAlign.center,
fontSize: 36, ),
fontWeight: FontWeight.bold, ),
color: Theme.of(context) Padding(
.primaryTextTheme! padding: EdgeInsets.only(top: 5),
.titleLarge! child: Text(
.color!, appDescription(context),
), style: TextStyle(
textAlign: TextAlign.center, fontSize: 16,
), fontWeight: FontWeight.w500,
), color: Theme.of(context)
Padding( .accentTextTheme.displayMedium!
padding: EdgeInsets.only(top: 5), .color!,
child: Text( ),
appDescription(context), textAlign: TextAlign.center,
style: TextStyle( ),
fontSize: 16, ),
fontWeight: FontWeight.w500, ],
color: Theme.of(context) ),
.accentTextTheme! Column(
.displayMedium! children: <Widget>[
.color, Text(
), S.of(context).please_make_selection,
textAlign: TextAlign.center, style: TextStyle(
), fontSize: 12,
), fontWeight: FontWeight.normal,
], color: Theme.of(context)
), .accentTextTheme.displayMedium!
Column( .color!,
children: <Widget>[ ),
Text( textAlign: TextAlign.center,
S.of(context).please_make_selection, ),
style: TextStyle( Padding(
fontSize: 12, padding: EdgeInsets.only(top: 24),
fontWeight: FontWeight.normal, child: PrimaryImageButton(
color: Theme.of(context) onPressed: () => Navigator.pushNamed(
.accentTextTheme! context, Routes.newWalletFromWelcome),
.displayMedium! image: newWalletImage,
.color, text: S.of(context).create_new,
), color: Theme.of(context)
textAlign: TextAlign.center, .accentTextTheme.titleSmall!
), .decorationColor!,
Padding( textColor: Theme.of(context)
padding: EdgeInsets.only(top: 24), .accentTextTheme.headlineSmall!
child: PrimaryImageButton( .decorationColor!,
onPressed: () => ),
Navigator.pushNamed(context, Routes.newWalletFromWelcome), ),
image: newWalletImage, Padding(
text: S.of(context).create_new, padding: EdgeInsets.only(top: 10),
color: Theme.of(context) child: PrimaryImageButton(
.accentTextTheme! onPressed: () {
.titleSmall! Navigator.pushNamed(
.decorationColor!, context, Routes.restoreOptions,
textColor: Theme.of(context) arguments: true);
.accentTextTheme! },
.headlineSmall! image: restoreWalletImage,
.decorationColor!, text: S.of(context).restore_wallet,
), color: Theme.of(context)
), .accentTextTheme.bodySmall!
Padding( .color!,
padding: EdgeInsets.only(top: 10), textColor: Theme.of(context)
child: PrimaryImageButton( .primaryTextTheme.titleLarge!
onPressed: () { .color!),
Navigator.pushNamed(context, Routes.restoreOptions, )
arguments: true); ],
}, )
image: restoreWalletImage, ],
text: S.of(context).restore_wallet,
color: Theme.of(context)
.accentTextTheme!
.bodySmall!
.color!,
textColor: Theme.of(context)
.primaryTextTheme!
.titleLarge!
.color!),
)
],
)
],
))
],
),
), ),
))); )));
} }

View file

@ -114,55 +114,66 @@ class AddressTextField extends StatelessWidget {
width: prefixIconWidth, width: prefixIconWidth,
height: prefixIconHeight, height: prefixIconHeight,
padding: EdgeInsets.only(top: 0), padding: EdgeInsets.only(top: 0),
child: InkWell( child: Semantics(
onTap: () async => _pasteAddress(context), label: S.of(context).paste,
child: Container( child: InkWell(
padding: EdgeInsets.all(8), onTap: () async => _pasteAddress(context),
decoration: BoxDecoration( child: Container(
color: buttonColor ?? padding: EdgeInsets.all(8),
Theme.of(context) decoration: BoxDecoration(
.accentTextTheme! color: buttonColor ??
Theme.of(context)
.accentTextTheme
!
.titleLarge! .titleLarge!
.color!, .color!,
borderRadius: borderRadius:
BorderRadius.all(Radius.circular(6))), BorderRadius.all(Radius.circular(6))),
child: Image.asset( child: Image.asset(
'assets/images/paste_ios.png', 'assets/images/paste_ios.png',
color: iconColor ?? color: iconColor ??
Theme.of(context) Theme.of(context)
.primaryTextTheme! .primaryTextTheme
!
.headlineMedium! .headlineMedium!
.decorationColor!, .decorationColor!,
)), )),
),
)), )),
], ],
if (this.options.contains(AddressTextFieldOption.qrCode) && DeviceInfo.instance.isMobile) if (this.options.contains(AddressTextFieldOption.qrCode) &&
...[ DeviceInfo.instance.isMobile) ...[
Container( Container(
width: prefixIconWidth, width: prefixIconWidth,
height: prefixIconHeight, height: prefixIconHeight,
padding: EdgeInsets.only(top: 0), padding: EdgeInsets.only(top: 0),
child: InkWell( child: Semantics(
onTap: () async => _presentQRScanner(context), label: S.of(context).scan_qr_code,
child: Container( child: InkWell(
padding: EdgeInsets.all(8), onTap: () async => _presentQRScanner(context),
decoration: BoxDecoration( child: Container(
color: buttonColor ?? padding: EdgeInsets.all(8),
Theme.of(context) decoration: BoxDecoration(
.accentTextTheme color: buttonColor ??
Theme.of(context)
.accentTextTheme
.titleLarge! .titleLarge!
.color!, .color!,
borderRadius: borderRadius:
BorderRadius.all(Radius.circular(6))), BorderRadius.all(Radius.circular(6))),
child: Image.asset( child: Image.asset(
'assets/images/qr_code_icon.png', 'assets/images/qr_code_icon.png',
color: iconColor ?? color: iconColor ??
Theme.of(context) Theme.of(context)
.primaryTextTheme!.headlineMedium! .primaryTextTheme
.decorationColor!, !.headlineMedium!
)), .decorationColor!,
)),
),
)) ))
] else SizedBox(width: 5), ] else
SizedBox(width: 5),
if (this if (this
.options .options
.contains(AddressTextFieldOption.addressBook)) ...[ .contains(AddressTextFieldOption.addressBook)) ...[
@ -170,26 +181,32 @@ class AddressTextField extends StatelessWidget {
width: prefixIconWidth, width: prefixIconWidth,
height: prefixIconHeight, height: prefixIconHeight,
padding: EdgeInsets.only(top: 0), padding: EdgeInsets.only(top: 0),
child: InkWell( child: Semantics(
onTap: () async => _presetAddressBookPicker(context), label: S.of(context).address_book,
child: Container( child: InkWell(
padding: EdgeInsets.all(8), onTap: () async =>
decoration: BoxDecoration( _presetAddressBookPicker(context),
color: buttonColor ?? child: Container(
Theme.of(context) padding: EdgeInsets.all(8),
.accentTextTheme! decoration: BoxDecoration(
color: buttonColor ??
Theme.of(context)
.accentTextTheme
!
.titleLarge! .titleLarge!
.color!, .color!,
borderRadius: borderRadius:
BorderRadius.all(Radius.circular(6))), BorderRadius.all(Radius.circular(6))),
child: Image.asset( child: Image.asset(
'assets/images/open_book.png', 'assets/images/open_book.png',
color: iconColor ?? color: iconColor ??
Theme.of(context) Theme.of(context)
.primaryTextTheme! .primaryTextTheme
!
.headlineMedium! .headlineMedium!
.decorationColor!, .decorationColor!,
)), )),
),
)) ))
] ]
], ],

View file

@ -1,9 +1,9 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class MarketPlaceItem extends StatelessWidget { class DashBoardRoundedCardWidget extends StatelessWidget {
MarketPlaceItem({ DashBoardRoundedCardWidget({
required this.onTap, required this.onTap,
required this.title, required this.title,
required this.subTitle, required this.subTitle,

View file

@ -1,6 +1,7 @@
import 'package:auto_size_text/auto_size_text.dart'; import 'package:auto_size_text/auto_size_text.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:cake_wallet/palette.dart'; import 'package:cake_wallet/palette.dart';
import 'package:cake_wallet/generated/i18n.dart';
class IntroducingCard extends StatelessWidget { class IntroducingCard extends StatelessWidget {
IntroducingCard( IntroducingCard(
@ -70,7 +71,7 @@ class IntroducingCard extends StatelessWidget {
Padding( Padding(
padding: const EdgeInsets.fromLTRB(0,16,16,0), padding: const EdgeInsets.fromLTRB(0,16,16,0),
child: Semantics( child: Semantics(
label: 'Close', label: S.of(context).close,
child: GestureDetector( child: GestureDetector(
onTap: closeCard, onTap: closeCard,
child: Container( child: Container(

View file

@ -1,5 +1,6 @@
// ignore_for_file: deprecated_member_use // ignore_for_file: deprecated_member_use
import 'package:cake_wallet/src/widgets/search_bar_widget.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:cw_core/currency.dart'; import 'package:cw_core/currency.dart';
@ -158,37 +159,7 @@ class _PickerState<Item> extends State<Picker<Item>> {
if (widget.hintText != null) if (widget.hintText != null)
Padding( Padding(
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
child: TextFormField( child: SearchBarWidget(searchController: searchController),
controller: searchController,
style: TextStyle(
color: Theme.of(context)
.primaryTextTheme!
.titleLarge!
.color!),
decoration: InputDecoration(
hintText: widget.hintText,
prefixIcon:
Image.asset("assets/images/search_icon.png"),
filled: true,
fillColor: Theme.of(context)
.accentTextTheme!
.displaySmall!
.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( Divider(
color: Theme.of(context) color: Theme.of(context)

View file

@ -0,0 +1,91 @@
import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/src/widgets/picker_wrapper_widget.dart';
class PickerInnerWrapperWidget extends StatelessWidget {
PickerInnerWrapperWidget(
{required this.children, this.title, this.itemsHeight});
final List<Widget> children;
final String? title;
final double? itemsHeight;
@override
Widget build(BuildContext context) {
final mq = MediaQuery.of(context);
final bottom = mq.viewInsets.bottom;
final height = mq.size.height - bottom;
double containerHeight = height * 0.65;
if (bottom > 0) {
// increase a bit or it gets too squished in the top
containerHeight = height * 0.75;
}
if (title != null) {
return PickerWrapperWidget(
hasTitle: true,
children: <Widget>[
Container(
padding: EdgeInsets.symmetric(horizontal: 24),
child: Text(
title!,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
fontFamily: 'Lato',
decoration: TextDecoration.none,
color: Colors.white),
),
),
Padding(
padding: EdgeInsets.symmetric(horizontal: 24),
child: ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(14)),
child: Container(
color: Theme.of(context).textTheme.displayLarge!.decorationColor!,
child: ConstrainedBox(
constraints: BoxConstraints(
maxHeight:
itemsHeight != null && itemsHeight! <= containerHeight
? itemsHeight!
: containerHeight,
maxWidth: ResponsiveLayoutUtil.kPopupWidth,
),
child: Column(
children: children,
),
),
),
),
)
],
);
}
return PickerWrapperWidget(
hasTitle: false,
children: <Widget>[
Padding(
padding: EdgeInsets.symmetric(horizontal: 24),
child: ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(14)),
child: Container(
color: Theme.of(context).textTheme.displayLarge!.decorationColor!,
child: ConstrainedBox(
constraints: BoxConstraints(
maxHeight: containerHeight,
maxWidth: ResponsiveLayoutUtil.kPopupWidth,
),
child: Column(
children: children,
),
),
),
),
)
],
);
}
}

View file

@ -0,0 +1,40 @@
import 'package:flutter/material.dart';
import 'package:cake_wallet/generated/i18n.dart';
class SearchBarWidget extends StatelessWidget {
const SearchBarWidget({
required this.searchController,
this.hintText,
this.borderRadius = 14,
});
final TextEditingController searchController;
final String? hintText;
final double borderRadius;
@override
Widget build(BuildContext context) {
return TextFormField(
controller: searchController,
style: TextStyle(color: Theme.of(context).primaryTextTheme!.titleLarge!.color!),
decoration: InputDecoration(
hintText: hintText ?? S.of(context).search_currency,
prefixIcon: Image.asset("assets/images/search_icon.png"),
filled: true,
fillColor: Theme.of(context).accentTextTheme!.displaySmall!.color!,
alignLabelWithHint: false,
contentPadding: const EdgeInsets.symmetric(vertical: 4, horizontal: 16),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(borderRadius),
borderSide: const BorderSide(
color: Colors.transparent,
)),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(borderRadius),
borderSide: const BorderSide(
color: Colors.transparent,
)),
),
);
}
}

View file

@ -231,7 +231,7 @@ class SectionStandardList extends StatelessWidget {
return Container(); return Container();
} }
return StandardListSeparator(padding: EdgeInsets.only(left: 24)); return StandardListSeparator(padding: dividerPadding);
}, },
itemCount: totalRows.length, itemCount: totalRows.length,
itemBuilder: (_, index) => totalRows[index]); itemBuilder: (_, index) => totalRows[index]);

View file

@ -146,15 +146,13 @@ abstract class SettingsStoreBase with Store {
}); });
} }
reaction( reaction((_) => disableBuy,
(_) => disableBuy, (bool disableBuy) => sharedPreferences.setBool(PreferencesKey.disableBuyKey, disableBuy));
(bool disableBuy) => sharedPreferences.setBool(
PreferencesKey.disableBuyKey, disableBuy));
reaction( reaction(
(_) => disableSell, (_) => disableSell,
(bool disableSell) => sharedPreferences.setBool( (bool disableSell) =>
PreferencesKey.disableSellKey, disableSell)); sharedPreferences.setBool(PreferencesKey.disableSellKey, disableSell));
reaction( reaction(
(_) => fiatApiMode, (_) => fiatApiMode,
@ -352,12 +350,9 @@ abstract class SettingsStoreBase with Store {
// FIX-ME: Check for which default value we should have here // FIX-ME: Check for which default value we should have here
final shouldSaveRecipientAddress = final shouldSaveRecipientAddress =
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 disableBuy = final disableSell = sharedPreferences.getBool(PreferencesKey.disableSellKey) ?? false;
sharedPreferences.getBool(PreferencesKey.disableBuyKey) ?? false;
final disableSell =
sharedPreferences.getBool(PreferencesKey.disableSellKey) ?? false;
final currentFiatApiMode = FiatApiMode.deserialize( final currentFiatApiMode = FiatApiMode.deserialize(
raw: sharedPreferences.getInt(PreferencesKey.currentFiatApiModeKey) ?? raw: sharedPreferences.getInt(PreferencesKey.currentFiatApiModeKey) ??
FiatApiMode.enabled.raw); FiatApiMode.enabled.raw);
@ -494,16 +489,14 @@ abstract class SettingsStoreBase with Store {
useTOTP2FA = sharedPreferences.getBool(PreferencesKey.useTOTP2FA) ?? useTOTP2FA; useTOTP2FA = sharedPreferences.getBool(PreferencesKey.useTOTP2FA) ?? useTOTP2FA;
numberOfFailedTokenTrials = numberOfFailedTokenTrials =
sharedPreferences.getInt(PreferencesKey.failedTotpTokenTrials) ?? numberOfFailedTokenTrials; sharedPreferences.getInt(PreferencesKey.failedTotpTokenTrials) ?? numberOfFailedTokenTrials;
sharedPreferences.getBool(PreferencesKey.shouldSaveRecipientAddressKey) ?? shouldSaveRecipientAddress; sharedPreferences.getBool(PreferencesKey.shouldSaveRecipientAddressKey) ??
isAppSecure = shouldSaveRecipientAddress;
sharedPreferences.getBool(PreferencesKey.isAppSecureKey) ?? isAppSecure; isAppSecure = sharedPreferences.getBool(PreferencesKey.isAppSecureKey) ?? isAppSecure;
disableBuy = disableBuy = sharedPreferences.getBool(PreferencesKey.disableBuyKey) ?? disableBuy;
sharedPreferences.getBool(PreferencesKey.disableBuyKey) ?? disableBuy; disableSell = sharedPreferences.getBool(PreferencesKey.disableSellKey) ?? disableSell;
disableSell = allowBiometricalAuthentication =
sharedPreferences.getBool(PreferencesKey.disableSellKey) ?? disableSell; sharedPreferences.getBool(PreferencesKey.allowBiometricalAuthenticationKey) ??
allowBiometricalAuthentication = sharedPreferences allowBiometricalAuthentication;
.getBool(PreferencesKey.allowBiometricalAuthenticationKey) ??
allowBiometricalAuthentication;
shouldShowMarketPlaceInDashboard = shouldShowMarketPlaceInDashboard =
sharedPreferences.getBool(PreferencesKey.shouldShowMarketPlaceInDashboard) ?? sharedPreferences.getBool(PreferencesKey.shouldShowMarketPlaceInDashboard) ??
shouldShowMarketPlaceInDashboard; shouldShowMarketPlaceInDashboard;

View file

@ -0,0 +1,39 @@
import 'dart:io';
import 'package:package_info/package_info.dart';
enum DistributionType { googleplay, github, appstore, fdroid }
class DistributionInfo {
DistributionInfo._();
static DistributionInfo get instance => DistributionInfo._();
Future<String> getDistributionPath() async {
final isPlayStore = await isInstalledFromPlayStore();
final distributionPath = _getDistributionPath(isPlayStore);
return distributionPath.name;
}
DistributionType _getDistributionPath(bool isPlayStore) {
if (isPlayStore) {
return DistributionType.googleplay;
} else if (Platform.isAndroid) {
return DistributionType.github;
} else if (Platform.isIOS) {
return DistributionType.appstore;
} else {
return DistributionType.github;
}
}
Future<bool> isInstalledFromPlayStore() async {
try {
final packageInfo = await PackageInfo.fromPlatform();
return packageInfo.packageName == 'com.android.vending';
} catch (e) {
print('Error: $e');
return false;
}
}
}

View file

@ -0,0 +1,3 @@
class FeatureFlag {
static const bool isCakePayEnabled = false;
}

View file

@ -1,22 +1,32 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class ResponsiveLayoutUtil { class ResponsiveLayoutUtil {
static const double _kMobileThreshold = 768; static const double _kMobileThreshold = 550;
static const double kDesktopMaxWidthConstraint = 400; static const double kDesktopMaxWidthConstraint = 400;
static const double kDesktopMaxDashBoardWidthConstraint = 900;
static const double kPopupWidth = 400; static const double kPopupWidth = 400;
static const double kPopupSpaceHeight = 100; static const double kPopupSpaceHeight = 100;
static const _kIpadMaxWidth = 2560.0;
const ResponsiveLayoutUtil._(); const ResponsiveLayoutUtil._();
static final instance = ResponsiveLayoutUtil._(); static final instance = ResponsiveLayoutUtil._();
bool get isMobile => bool get isMobile =>
MediaQueryData.fromWindow(WidgetsBinding.instance.window).size.width < _kMobileThreshold; MediaQueryData.fromWindow(WidgetsBinding.instance.window).size.shortestSide <=
_kMobileThreshold;
bool get isIpad { bool shouldRenderMobileUI() {
final width = MediaQueryData.fromWindow(WidgetsBinding.instance.window).size.width; final mediaQuery = MediaQueryData.fromWindow(WidgetsBinding.instance.window);
return width >= _kMobileThreshold && !(width > _kIpadMaxWidth); final orientation = mediaQuery.orientation;
final width = mediaQuery.size.width;
final height = mediaQuery.size.height;
if (isMobile ||
(orientation == Orientation.portrait && width < height) ||
(orientation == Orientation.landscape && width < height)) {
return true;
} else {
return false;
}
} }
/// Returns dynamic size. /// Returns dynamic size.

View file

@ -4,9 +4,9 @@ part 'desktop_sidebar_view_model.g.dart';
enum SidebarItem { enum SidebarItem {
dashboard, dashboard,
transactions,
support, support,
settings, settings,
transactions;
} }
class DesktopSidebarViewModel = DesktopSidebarViewModelBase with _$DesktopSidebarViewModel; class DesktopSidebarViewModel = DesktopSidebarViewModelBase with _$DesktopSidebarViewModel;

View file

@ -36,7 +36,8 @@ abstract class ExchangeTradeViewModelBase with Store {
_provider = XMRTOExchangeProvider(); _provider = XMRTOExchangeProvider();
break; break;
case ExchangeProviderDescription.changeNow: case ExchangeProviderDescription.changeNow:
_provider = ChangeNowExchangeProvider(); _provider =
ChangeNowExchangeProvider(settingsStore: sendViewModel.balanceViewModel.settingsStore);
break; break;
case ExchangeProviderDescription.morphToken: case ExchangeProviderDescription.morphToken:
_provider = MorphTokenExchangeProvider(trades: trades); _provider = MorphTokenExchangeProvider(trades: trades);

View file

@ -130,7 +130,7 @@ abstract class ExchangeViewModelBase with Store {
final SharedPreferences sharedPreferences; final SharedPreferences sharedPreferences;
List<ExchangeProvider> get _allProviders => [ List<ExchangeProvider> get _allProviders => [
ChangeNowExchangeProvider(), ChangeNowExchangeProvider(settingsStore: _settingsStore),
SideShiftExchangeProvider(), SideShiftExchangeProvider(),
SimpleSwapExchangeProvider(), SimpleSwapExchangeProvider(),
TrocadorExchangeProvider(useTorOnly: _useTorOnly), TrocadorExchangeProvider(useTorOnly: _useTorOnly),
@ -221,7 +221,7 @@ abstract class ExchangeViewModelBase with Store {
bool get hasAllAmount => bool get hasAllAmount =>
wallet.type == WalletType.bitcoin && depositCurrency == wallet.currency; (wallet.type == WalletType.bitcoin || wallet.type == WalletType.litecoin) && depositCurrency == wallet.currency;
bool get isMoneroWallet => wallet.type == WalletType.monero; bool get isMoneroWallet => wallet.type == WalletType.monero;
@ -566,7 +566,7 @@ abstract class ExchangeViewModelBase with Store {
@action @action
void calculateDepositAllAmount() { void calculateDepositAllAmount() {
if (wallet.type == WalletType.bitcoin) { if (wallet.type == WalletType.bitcoin || wallet.type == WalletType.litecoin) {
final availableBalance = wallet.balance[wallet.currency]!.available; final availableBalance = wallet.balance[wallet.currency]!.available;
final priority = _settingsStore.priority[wallet.type]!; final priority = _settingsStore.priority[wallet.type]!;
final fee = wallet.calculateEstimatedFee(priority, null); final fee = wallet.calculateEstimatedFee(priority, null);

View file

@ -24,6 +24,7 @@ abstract class RescanViewModelBase with Store {
Future<void> rescanCurrentWallet({required int restoreHeight}) async { Future<void> rescanCurrentWallet({required int restoreHeight}) async {
state = RescanWalletState.rescaning; state = RescanWalletState.rescaning;
await _wallet.rescan(height: restoreHeight); await _wallet.rescan(height: restoreHeight);
_wallet.transactionHistory.clear();
state = RescanWalletState.none; state = RescanWalletState.none;
} }
} }

View file

@ -40,7 +40,7 @@ abstract class TradeDetailsViewModelBase with Store {
_provider = XMRTOExchangeProvider(); _provider = XMRTOExchangeProvider();
break; break;
case ExchangeProviderDescription.changeNow: case ExchangeProviderDescription.changeNow:
_provider = ChangeNowExchangeProvider(); _provider = ChangeNowExchangeProvider(settingsStore: settingsStore);
break; break;
case ExchangeProviderDescription.morphToken: case ExchangeProviderDescription.morphToken:
_provider = MorphTokenExchangeProvider(trades: trades); _provider = MorphTokenExchangeProvider(trades: trades);

View file

@ -0,0 +1,55 @@
import 'package:cake_wallet/core/wallet_loading_service.dart';
import 'package:cake_wallet/view_model/wallet_list/wallet_list_view_model.dart';
import 'package:mobx/mobx.dart';
import 'package:cake_wallet/di.dart';
import 'package:cw_core/wallet_service.dart';
import 'package:cake_wallet/view_model/wallet_list/wallet_list_item.dart';
part 'wallet_edit_view_model.g.dart';
class WalletEditViewModel = WalletEditViewModelBase with _$WalletEditViewModel;
abstract class WalletEditViewModelState {}
class WalletEditViewModelInitialState extends WalletEditViewModelState {}
class WalletEditRenamePending extends WalletEditViewModelState {}
class WalletEditDeletePending extends WalletEditViewModelState {}
abstract class WalletEditViewModelBase with Store {
WalletEditViewModelBase(this._walletListViewModel, this._walletLoadingService)
: state = WalletEditViewModelInitialState(),
newName = '';
@observable
WalletEditViewModelState state;
@observable
String newName;
final WalletListViewModel _walletListViewModel;
final WalletLoadingService _walletLoadingService;
@action
Future<void> changeName(WalletListItem walletItem) async {
state = WalletEditRenamePending();
await _walletLoadingService.renameWallet(
walletItem.type, walletItem.name, newName);
_walletListViewModel.updateList();
}
@action
Future<void> remove(WalletListItem wallet) async {
state = WalletEditDeletePending();
final walletService = getIt.get<WalletService>(param1: wallet.type);
await walletService.remove(wallet.name);
resetState();
_walletListViewModel.updateList();
}
@action
void resetState() {
state = WalletEditViewModelInitialState();
}
}

View file

@ -1,14 +1,13 @@
import 'package:cake_wallet/core/auth_service.dart'; import 'package:cake_wallet/core/auth_service.dart';
import 'package:cake_wallet/core/wallet_loading_service.dart'; import 'package:cake_wallet/core/wallet_loading_service.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/balance.dart'; import 'package:cw_core/balance.dart';
import 'package:cw_core/transaction_history.dart'; import 'package:cw_core/transaction_history.dart';
import 'package:cw_core/transaction_info.dart'; import 'package:cw_core/transaction_info.dart';
import 'package:cw_core/wallet_base.dart'; import 'package:cw_core/wallet_base.dart';
import 'package:hive/hive.dart'; import 'package:hive/hive.dart';
import 'package:mobx/mobx.dart'; import 'package:mobx/mobx.dart';
import 'package:cake_wallet/di.dart';
import 'package:cake_wallet/store/app_store.dart'; import 'package:cake_wallet/store/app_store.dart';
import 'package:cw_core/wallet_service.dart';
import 'package:cake_wallet/view_model/wallet_list/wallet_list_item.dart'; import 'package:cake_wallet/view_model/wallet_list/wallet_list_item.dart';
import 'package:cw_core/wallet_info.dart'; import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/wallet_type.dart'; import 'package:cw_core/wallet_type.dart';
@ -25,8 +24,8 @@ abstract class WalletListViewModelBase with Store {
this._walletLoadingService, this._walletLoadingService,
this._authService, this._authService,
) : wallets = ObservableList<WalletListItem>() { ) : wallets = ObservableList<WalletListItem>() {
_updateList(); updateList();
reaction((_) => _appStore.wallet, (_) => _updateList()); reaction((_) => _appStore.wallet, (_) => updateList());
} }
@observable @observable
@ -41,20 +40,14 @@ abstract class WalletListViewModelBase with Store {
@action @action
Future<void> loadWallet(WalletListItem walletItem) async { Future<void> loadWallet(WalletListItem walletItem) async {
final wallet = await _walletLoadingService.load(walletItem.type, walletItem.name); final wallet =
await _walletLoadingService.load(walletItem.type, walletItem.name);
_appStore.changeCurrentWallet(wallet); _appStore.changeCurrentWallet(wallet);
_updateList();
} }
@action @action
Future<void> remove(WalletListItem wallet) async { void updateList() {
final walletService = getIt.get<WalletService>(param1: wallet.type);
await walletService.remove(wallet.name);
await _walletInfoSource.delete(wallet.key);
_updateList();
}
void _updateList() {
wallets.clear(); wallets.clear();
wallets.addAll( wallets.addAll(
_walletInfoSource.values.map( _walletInfoSource.values.map(
@ -62,7 +55,8 @@ abstract class WalletListViewModelBase with Store {
name: info.name, name: info.name,
type: info.type, type: info.type,
key: info.key, key: info.key,
isCurrent: info.name == _appStore.wallet!.name && info.type == _appStore.wallet!.type, isCurrent: info.name == _appStore.wallet!.name &&
info.type == _appStore.wallet!.type,
isEnabled: availableWalletTypes.contains(info.type), isEnabled: availableWalletTypes.contains(info.type),
), ),
), ),

View file

@ -115,7 +115,7 @@ SPEC CHECKSUMS:
platform_device_id_macos: f763bb55f088be804d61b96eb4710b8ab6598e94 platform_device_id_macos: f763bb55f088be804d61b96eb4710b8ab6598e94
ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825 ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825
share_plus_macos: 853ee48e7dce06b633998ca0735d482dd671ade4 share_plus_macos: 853ee48e7dce06b633998ca0735d482dd671ade4
shared_preferences_foundation: e2dae3258e06f44cc55f49d42024fd8dd03c590c shared_preferences_foundation: 5b919d13b803cadd15ed2dc053125c68730e5126
url_launcher_macos: 5335912b679c073563f29d89d33d10d459f95451 url_launcher_macos: 5335912b679c073563f29d89d33d10d459f95451
wakelock_macos: bc3f2a9bd8d2e6c89fee1e1822e7ddac3bd004a9 wakelock_macos: bc3f2a9bd8d2e6c89fee1e1822e7ddac3bd004a9

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -5,19 +5,14 @@
"please_make_selection": "Don Allah zaɓi ƙasa don ƙirƙira ko dawo da kwalinku.", "please_make_selection": "Don Allah zaɓi ƙasa don ƙirƙira ko dawo da kwalinku.",
"create_new": "Ƙirƙira Sabon Kwalinku", "create_new": "Ƙirƙira Sabon Kwalinku",
"restore_wallet": "Dawo da Kwalinku", "restore_wallet": "Dawo da Kwalinku",
"monero_com": "Monero.com ta Cake Wallet", "monero_com": "Monero.com ta Cake Wallet",
"monero_com_wallet_text": "Aikace-aikacen e-wallet ga Monero", "monero_com_wallet_text": "Aikace-aikacen e-wallet ga Monero",
"haven_app": "Haven da Cake Wallet", "haven_app": "Haven da Cake Wallet",
"haven_app_wallet_text": "Aikace-aikacen e-wallet ga Haven", "haven_app_wallet_text": "Aikace-aikacen e-wallet ga Haven",
"accounts": "Lissafi", "accounts": "Lissafi",
"edit": "Gyara", "edit": "Gyara",
"account": "Asusu", "account": "Asusu",
"add": "Ƙara", "add": "Ƙara",
"address_book": "Littafin adireshi", "address_book": "Littafin adireshi",
"contact": "Tuntuɓar", "contact": "Tuntuɓar",
"please_select": "Don Allah zaɓi:", "please_select": "Don Allah zaɓi:",
@ -28,13 +23,9 @@
"save": "Ajiye", "save": "Ajiye",
"address_remove_contact": "Cire lamba", "address_remove_contact": "Cire lamba",
"address_remove_content": "Kuna tabbatar kuna so ku cire wannan Contact?", "address_remove_content": "Kuna tabbatar kuna so ku cire wannan Contact?",
"authenticated": "Ingantacce", "authenticated": "Ingantacce",
"authentication": "Tabbatarwa", "authentication": "Tabbatarwa",
"failed_authentication": "Binne wajen shiga. ${state_error}", "failed_authentication": "Binne wajen shiga. ${state_error}",
"wallet_menu": "Menu", "wallet_menu": "Menu",
"Blocks_remaining": "${status} Katanga ya rage", "Blocks_remaining": "${status} Katanga ya rage",
"please_try_to_connect_to_another_node": "Don Allah yi ƙoƙarin haɗa da wani node", "please_try_to_connect_to_another_node": "Don Allah yi ƙoƙarin haɗa da wani node",
@ -62,8 +53,6 @@
"address_book_menu": "Littafin adireshi", "address_book_menu": "Littafin adireshi",
"reconnection": "Sake haɗawa", "reconnection": "Sake haɗawa",
"reconnect_alert_text": "Shin kun tabbata kuna son sake haɗawa?", "reconnect_alert_text": "Shin kun tabbata kuna son sake haɗawa?",
"exchange": "Exchange", "exchange": "Exchange",
"clear": "Share", "clear": "Share",
"refund_address": "Adireshin maidowa", "refund_address": "Adireshin maidowa",
@ -80,7 +69,6 @@
"change_currency": "Canja Kuɗi", "change_currency": "Canja Kuɗi",
"overwrite_amount": "Rubuta adadin", "overwrite_amount": "Rubuta adadin",
"qr_payment_amount": "Wannan QR code yana da adadin kuɗi. Kuna so ku overwrite wannan adadi?", "qr_payment_amount": "Wannan QR code yana da adadin kuɗi. Kuna so ku overwrite wannan adadi?",
"copy_id": "Kwafi ID", "copy_id": "Kwafi ID",
"exchange_result_write_down_trade_id": "Da fatan za a kwafa ko rubuta ID ɗin ciniki don ci gaba.", "exchange_result_write_down_trade_id": "Da fatan za a kwafa ko rubuta ID ɗin ciniki don ci gaba.",
"trade_id": "ID na kasuwanci:", "trade_id": "ID na kasuwanci:",
@ -106,20 +94,13 @@
"time": "${minutes}m ${seconds}s", "time": "${minutes}m ${seconds}s",
"send_xmr": "Aika XMR", "send_xmr": "Aika XMR",
"exchange_new_template": "Sabon template", "exchange_new_template": "Sabon template",
"faq": "FAQ", "faq": "FAQ",
"enter_your_pin": "Shigar da PIN", "enter_your_pin": "Shigar da PIN",
"loading_your_wallet": "Ana loda walat ɗin ku", "loading_your_wallet": "Ana loda walat ɗin ku",
"new_wallet": "Sabuwar Wallet", "new_wallet": "Sabuwar Wallet",
"wallet_name": "Sunan walat", "wallet_name": "Sunan walat",
"continue_text": "Ci gaba", "continue_text": "Ci gaba",
"choose_wallet_currency": "Da fatan za a zaɓi kuɗin walat:", "choose_wallet_currency": "Da fatan za a zaɓi kuɗin walat:",
"node_new": "Sabon Node", "node_new": "Sabon Node",
"node_address": "Address Node", "node_address": "Address Node",
"node_port": "Node tashar jiragen ruwa", "node_port": "Node tashar jiragen ruwa",
@ -140,24 +121,18 @@
"node_connection_successful": "Haɗin ya yi nasara", "node_connection_successful": "Haɗin ya yi nasara",
"node_connection_failed": "Haɗin ya gaza", "node_connection_failed": "Haɗin ya gaza",
"new_node_testing": "Sabbin gwajin kumburi", "new_node_testing": "Sabbin gwajin kumburi",
"use": "Canja zuwa", "use": "Canja zuwa",
"digit_pin": "-lambar PIN", "digit_pin": "-lambar PIN",
"share_address": "Raba adireshin", "share_address": "Raba adireshin",
"receive_amount": "Adadi", "receive_amount": "Adadi",
"subaddresses": "Subaddresses", "subaddresses": "Subaddresses",
"addresses": "Addresses", "addresses": "Addresses",
"scan_qr_code": "Duba lambar QR don samun adireshin", "scan_qr_code": "Gani QR kodin",
"qr_fullscreen": "Matsa don buɗe lambar QR na cikakken allo", "qr_fullscreen": "Matsa don buɗe lambar QR na cikakken allo",
"rename": "Sake suna", "rename": "Sake suna",
"choose_account": "Zaɓi asusu", "choose_account": "Zaɓi asusu",
"create_new_account": "Ƙirƙiri sabon asusu", "create_new_account": "Ƙirƙiri sabon asusu",
"accounts_subaddresses": "Accounts da subaddresses", "accounts_subaddresses": "Accounts da subaddresses",
"restore_restore_wallet": "Maida Wallet", "restore_restore_wallet": "Maida Wallet",
"restore_title_from_seed_keys": "Dawo da iri/maɓallai", "restore_title_from_seed_keys": "Dawo da iri/maɓallai",
"restore_description_from_seed_keys": "Maido da walat ɗin ku daga iri/maɓallan da kuka adana don amintaccen wuri", "restore_description_from_seed_keys": "Maido da walat ɗin ku daga iri/maɓallan da kuka adana don amintaccen wuri",
@ -181,14 +156,10 @@
"restore_bitcoin_description_from_keys": "Dawo da kwalinku daga WIF string dake generate daga maɓallan sirri", "restore_bitcoin_description_from_keys": "Dawo da kwalinku daga WIF string dake generate daga maɓallan sirri",
"restore_bitcoin_title_from_keys": "Dawo daga WIF", "restore_bitcoin_title_from_keys": "Dawo daga WIF",
"restore_from_date_or_blockheight": "Don Allah shigar da wata kwanan a kafin ku ƙirƙirar wannan kwalinku. Ko idan kun san blockheight, don Allah shigar da shi", "restore_from_date_or_blockheight": "Don Allah shigar da wata kwanan a kafin ku ƙirƙirar wannan kwalinku. Ko idan kun san blockheight, don Allah shigar da shi",
"seed_reminder": "Don Allah rubuta wadannan in case ka manta ko ka sake kwallon wayarka", "seed_reminder": "Don Allah rubuta wadannan in case ka manta ko ka sake kwallon wayarka",
"seed_title": "iri", "seed_title": "iri",
"seed_share": "Raba iri", "seed_share": "Raba iri",
"copy": "Kwafi", "copy": "Kwafi",
"seed_language_choose": "Don Allah zaɓi harshen seed:", "seed_language_choose": "Don Allah zaɓi harshen seed:",
"seed_choose": "Zaɓi harshen seed", "seed_choose": "Zaɓi harshen seed",
"seed_language_next": "Na gaba", "seed_language_next": "Na gaba",
@ -202,8 +173,6 @@
"seed_language_spanish": "Spanish", "seed_language_spanish": "Spanish",
"seed_language_french": "Faransanci", "seed_language_french": "Faransanci",
"seed_language_italian": "Italiyanci", "seed_language_italian": "Italiyanci",
"send_title": "Aika", "send_title": "Aika",
"send_your_wallet": "Walat ɗin ku", "send_your_wallet": "Walat ɗin ku",
"send_address": "${cryptoCurrency} address", "send_address": "${cryptoCurrency} address",
@ -219,11 +188,9 @@
"send_amount": "Adadi:", "send_amount": "Adadi:",
"send_fee": "Kudin:", "send_fee": "Kudin:",
"send_name": "Sunan", "send_name": "Sunan",
"send_got_it": "Gama", "got_it": "Gama",
"send_sending": "Aika...", "send_sending": "Aika...",
"send_success": "${crypto} kwalinku ya aika da nasara", "send_success": "${crypto} kwalinku ya aika da nasara",
"settings_title": "Saitunan", "settings_title": "Saitunan",
"settings_nodes": "Nodes", "settings_nodes": "Nodes",
"settings_current_node": "Node yanzu", "settings_current_node": "Node yanzu",
@ -247,13 +214,9 @@
"settings_support": "Taimako", "settings_support": "Taimako",
"settings_terms_and_conditions": "Sharuɗɗa da Ka'idoji", "settings_terms_and_conditions": "Sharuɗɗa da Ka'idoji",
"pin_is_incorrect": "PIN ba daidai ba ne", "pin_is_incorrect": "PIN ba daidai ba ne",
"setup_pin": "Saita PIN", "setup_pin": "Saita PIN",
"enter_your_pin_again": "Shigar da PIN ɗinku na sake", "enter_your_pin_again": "Shigar da PIN ɗinku na sake",
"setup_successful": "An saita PIN ɗinku da nasara!", "setup_successful": "An saita PIN ɗinku da nasara!",
"wallet_keys": "Iri/maɓalli na walat", "wallet_keys": "Iri/maɓalli na walat",
"wallet_seed": "kalmar sirri na walat", "wallet_seed": "kalmar sirri na walat",
"private_key": "Keɓaɓɓen maɓalli", "private_key": "Keɓaɓɓen maɓalli",
@ -263,17 +226,11 @@
"spend_key_private": "makullin biya (maɓallin kalmar sirri)", "spend_key_private": "makullin biya (maɓallin kalmar sirri)",
"spend_key_public": "makullin biya (maɓallin jama'a)", "spend_key_public": "makullin biya (maɓallin jama'a)",
"copied_key_to_clipboard": "An kwafa ${key} a cikin kwafin", "copied_key_to_clipboard": "An kwafa ${key} a cikin kwafin",
"new_subaddress_title": "Adireshin sabuwa", "new_subaddress_title": "Adireshin sabuwa",
"new_subaddress_label_name": "Lakabin suna", "new_subaddress_label_name": "Lakabin suna",
"new_subaddress_create": "Ƙirƙiri", "new_subaddress_create": "Ƙirƙiri",
"address_label": "Labari adireshi", "address_label": "Labari adireshi",
"subaddress_title": "Jagorar subaddress", "subaddress_title": "Jagorar subaddress",
"trade_details_title": "Bayanai game da kasuwancin", "trade_details_title": "Bayanai game da kasuwancin",
"trade_details_id": "ID", "trade_details_id": "ID",
"trade_details_state": "Matsayi", "trade_details_state": "Matsayi",
@ -282,11 +239,7 @@
"trade_details_created_at": "An ƙirƙira a", "trade_details_created_at": "An ƙirƙira a",
"trade_details_pair": "miji da matarsa", "trade_details_pair": "miji da matarsa",
"trade_details_copied": "${title} an kwafa zuwa cikin kwafin", "trade_details_copied": "${title} an kwafa zuwa cikin kwafin",
"trade_history_title": "Tarihin kasuwancin", "trade_history_title": "Tarihin kasuwancin",
"transaction_details_title": "Bayanai game da aikace-aikacen", "transaction_details_title": "Bayanai game da aikace-aikacen",
"transaction_details_transaction_id": "ID na kasuwanci", "transaction_details_transaction_id": "ID na kasuwanci",
"transaction_details_date": "Kwanan wata", "transaction_details_date": "Kwanan wata",
@ -295,28 +248,22 @@
"transaction_details_fee": "Kudin", "transaction_details_fee": "Kudin",
"transaction_details_copied": "${title} an kwafa zuwa cikin kwafin", "transaction_details_copied": "${title} an kwafa zuwa cikin kwafin",
"transaction_details_recipient_address": "Adireshin masu amfani", "transaction_details_recipient_address": "Adireshin masu amfani",
"wallet_list_title": "Monero walat", "wallet_list_title": "Monero walat",
"wallet_list_create_new_wallet": "Ƙirƙiri Sabon Wallet", "wallet_list_create_new_wallet": "Ƙirƙiri Sabon Wallet",
"wallet_list_edit_wallet" : "Gyara walat",
"wallet_list_wallet_name" : "Sunan walat",
"wallet_list_restore_wallet": "Maida Wallet", "wallet_list_restore_wallet": "Maida Wallet",
"wallet_list_load_wallet": "Ana loda wallet na Monero", "wallet_list_load_wallet": "Ana loda wallet na Monero",
"wallet_list_loading_wallet": "Ana loda ${wallet_name} walat", "wallet_list_loading_wallet": "Ana loda ${wallet_name} walat",
"wallet_list_failed_to_load": "An kasa loda ${wallet_name} walat. ${error}", "wallet_list_failed_to_load": "An kasa loda ${wallet_name} walat. ${error}",
"wallet_list_removing_wallet": "Cirewa ${wallet_name} walat", "wallet_list_removing_wallet": "Cirewa ${wallet_name} walat",
"wallet_list_failed_to_remove": "Ba a iya cirewa ${wallet_name} walat. ${error}", "wallet_list_failed_to_remove": "Ba a iya cirewa ${wallet_name} walat. ${error}",
"widgets_address": "Adireshin", "widgets_address": "Adireshin",
"widgets_restore_from_blockheight": "Sake dawo da daga blockheight", "widgets_restore_from_blockheight": "Sake dawo da daga blockheight",
"widgets_restore_from_date": "Sake dawo da daga kwanan wata", "widgets_restore_from_date": "Sake dawo da daga kwanan wata",
"widgets_or": "ko", "widgets_or": "ko",
"widgets_seed": "iri", "widgets_seed": "iri",
"router_no_route": "Babu wata hanya da aka bayyana don ${name}", "router_no_route": "Babu wata hanya da aka bayyana don ${name}",
"error_text_account_name": "Sunan ajiya zai iya ɗauka ne kawai da haruffa, lambobi\nkuma ya zama tsakanin 1 zuwa 15 haruffa", "error_text_account_name": "Sunan ajiya zai iya ɗauka ne kawai da haruffa, lambobi\nkuma ya zama tsakanin 1 zuwa 15 haruffa",
"error_text_contact_name": "Sunan kira ba zai iya ɗaukar ` , ' \" haruffa\nkuma ya zama tsakanin 1 zuwa 32 haruffa", "error_text_contact_name": "Sunan kira ba zai iya ɗaukar ` , ' \" haruffa\nkuma ya zama tsakanin 1 zuwa 32 haruffa",
"error_text_address": "Adireshin hujja ya kamata ya dace da irin\nna cryptocurrency", "error_text_address": "Adireshin hujja ya kamata ya dace da irin\nna cryptocurrency",
@ -334,21 +281,15 @@
"error_text_maximum_limit": "Kasuwanci ga ${provider} ba a yi ba. Adadin shine fiye da ƙimanin: ${max} ${currency}", "error_text_maximum_limit": "Kasuwanci ga ${provider} ba a yi ba. Adadin shine fiye da ƙimanin: ${max} ${currency}",
"error_text_limits_loading_failed": "Kasuwanci ga ${provider} ba a yi ba. An kasa saukewa masanan", "error_text_limits_loading_failed": "Kasuwanci ga ${provider} ba a yi ba. An kasa saukewa masanan",
"error_text_template": "Sunan na tushe da adireshin ba zai iya ɗaukar ` , ' \" haruffa\nkuma ya zama tsakanin 1 zuwa 106 haruffa", "error_text_template": "Sunan na tushe da adireshin ba zai iya ɗaukar ` , ' \" haruffa\nkuma ya zama tsakanin 1 zuwa 106 haruffa",
"auth_store_ban_timeout": "ban_timeout", "auth_store_ban_timeout": "ban_timeout",
"auth_store_banned_for": "An haramta don", "auth_store_banned_for": "An haramta don",
"auth_store_banned_minutes": "da minti", "auth_store_banned_minutes": "da minti",
"auth_store_incorrect_password": "PIN na gaskiya", "auth_store_incorrect_password": "PIN na gaskiya",
"wallet_store_monero_wallet": "Monero walat", "wallet_store_monero_wallet": "Monero walat",
"wallet_restoration_store_incorrect_seed_length": "kalmar sirrin iri ba daidai ba", "wallet_restoration_store_incorrect_seed_length": "kalmar sirrin iri ba daidai ba",
"full_balance": "DUKAN KUDI", "full_balance": "DUKAN KUDI",
"available_balance": "KUDI", "available_balance": "KUDI",
"hidden_balance": "BOYE KUDI", "hidden_balance": "BOYE KUDI",
"sync_status_syncronizing": "KWAFI", "sync_status_syncronizing": "KWAFI",
"sync_status_syncronized": "KYAU", "sync_status_syncronized": "KYAU",
"sync_status_not_connected": "BABU INTERNET", "sync_status_not_connected": "BABU INTERNET",
@ -357,21 +298,15 @@
"sync_status_connecting": "HADA", "sync_status_connecting": "HADA",
"sync_status_connected": "HANNU", "sync_status_connected": "HANNU",
"sync_status_attempting_sync": "KWAFI", "sync_status_attempting_sync": "KWAFI",
"transaction_priority_slow": "SAURI DA SAURI", "transaction_priority_slow": "SAURI DA SAURI",
"transaction_priority_regular": "SAURI NORMAL", "transaction_priority_regular": "SAURI NORMAL",
"transaction_priority_medium": "SAURI DA DADI", "transaction_priority_medium": "SAURI DA DADI",
"transaction_priority_fast": "sauri", "transaction_priority_fast": "sauri",
"transaction_priority_fastest": "mafi sauri", "transaction_priority_fastest": "mafi sauri",
"trade_for_not_created": "Ba a ƙirƙira ciniki don ${title} ba.", "trade_for_not_created": "Ba a ƙirƙira ciniki don ${title} ba.",
"trade_not_created": "Ba a ƙirƙira ciniki ba", "trade_not_created": "Ba a ƙirƙira ciniki ba",
"trade_id_not_found": "Ba a samo cinikin ${tradeId} na ${title} ba.", "trade_id_not_found": "Ba a samo cinikin ${tradeId} na ${title} ba.",
"trade_not_found": "Ba a sami ciniki ba.", "trade_not_found": "Ba a sami ciniki ba.",
"trade_state_pending": "Jira", "trade_state_pending": "Jira",
"trade_state_confirming": "Tabbatar", "trade_state_confirming": "Tabbatar",
"trade_state_trading": "Ciniki", "trade_state_trading": "Ciniki",
@ -386,59 +321,42 @@
"trade_state_timeout": "lokacin da ya ƙare", "trade_state_timeout": "lokacin da ya ƙare",
"trade_state_created": "an halicci", "trade_state_created": "an halicci",
"trade_state_finished": "an kammala", "trade_state_finished": "an kammala",
"change_language": "canja harshen", "change_language": "canja harshen",
"change_language_to": "canja harshen zuwa ${language}?", "change_language_to": "canja harshen zuwa ${language}?",
"paste": "Manna", "paste": "Manna",
"restore_from_seed_placeholder": "Da fatan za a shigar da ko manna maɓallin ku a nan", "restore_from_seed_placeholder": "Da fatan za a shigar da ko manna maɓallin ku a nan",
"add_new_word": "Ƙara kalma sabuwa", "add_new_word": "Ƙara kalma sabuwa",
"incorrect_seed": "rubutun da aka shigar ba shi da inganci.", "incorrect_seed": "rubutun da aka shigar ba shi da inganci.",
"biometric_auth_reason": "Duba hoton yatsa don tantancewa", "biometric_auth_reason": "Duba hoton yatsa don tantancewa",
"version": "Sigar ${currentVersion}", "version": "Sigar ${currentVersion}",
"openalias_alert_title": "An gano adireshin", "openalias_alert_title": "An gano adireshin",
"openalias_alert_content": "Zaka aika kuɗi zuwa \n${recipient_name}", "openalias_alert_content": "Zaka aika kuɗi zuwa \n${recipient_name}",
"card_address": "Adireshin:", "card_address": "Adireshin:",
"buy": "Sayi", "buy": "Sayi",
"sell": "sayar", "sell": "sayar",
"placeholder_transactions": "Za a nuna ma'amalolin ku anan", "placeholder_transactions": "Za a nuna ma'amalolin ku anan",
"placeholder_contacts": "Za a nuna lambobin sadarwar ku anan", "placeholder_contacts": "Za a nuna lambobin sadarwar ku anan",
"template": "Samfura", "template": "Samfura",
"confirm_delete_template": "Wannan aikin zai share wannan samfuri. Kuna so ku ci gaba?", "confirm_delete_template": "Wannan aikin zai share wannan samfuri. Kuna so ku ci gaba?",
"confirm_delete_wallet": "Wannan aikin zai share wannan walat. Kuna so ku ci gaba?", "confirm_delete_wallet": "Wannan aikin zai share wannan walat. Kuna so ku ci gaba?",
"picker_description": "Don zaɓar ChangeNOW ko MorphToken, da farko canja kasuwancin pair din ku", "picker_description": "Don zaɓar ChangeNOW ko MorphToken, da farko canja kasuwancin pair din ku",
"change_wallet_alert_title": "Canja walat yanzu", "change_wallet_alert_title": "Canja walat yanzu",
"change_wallet_alert_content": "Kana so ka canja walat yanzu zuwa ${wallet_name}?", "change_wallet_alert_content": "Kana so ka canja walat yanzu zuwa ${wallet_name}?",
"creating_new_wallet": "Haliccin walat sabuwa", "creating_new_wallet": "Haliccin walat sabuwa",
"creating_new_wallet_error": "Kuskure: ${description}", "creating_new_wallet_error": "Kuskure: ${description}",
"seed_alert_title": "Hankali", "seed_alert_title": "Hankali",
"seed_alert_content": "Irin ita ce kawai hanya don dawo da walat ɗin ku. Kun rubuta shi?", "seed_alert_content": "Irin ita ce kawai hanya don dawo da walat ɗin ku. Kun rubuta shi?",
"seed_alert_back": "juya baya", "seed_alert_back": "juya baya",
"seed_alert_yes": "E, Na yi", "seed_alert_yes": "E, Na yi",
"exchange_sync_alert_content": "Da fatan za a jira har sai an daidaita walat ɗin ku", "exchange_sync_alert_content": "Da fatan za a jira har sai an daidaita walat ɗin ku",
"pre_seed_title": "MUHIMMANCI", "pre_seed_title": "MUHIMMANCI",
"pre_seed_description": "A kan shafin nan za ku ga wata ƙungiya na ${words} kalmomi. Wannan shine tsarin daban-daban ku kuma na sirri kuma shine hanya ɗaya kadai don mai da purse dinku a cikin yanayin rasa ko rashin aiki. Yana da damar da kuke a cikin tabbatar da kuyi rubuta shi kuma kuyi ajiye shi a wuri na aminci wanda ya wuce wurin app na Cake Wallet.", "pre_seed_description": "A kan shafin nan za ku ga wata ƙungiya na ${words} kalmomi. Wannan shine tsarin daban-daban ku kuma na sirri kuma shine hanya ɗaya kadai don mai da purse dinku a cikin yanayin rasa ko rashin aiki. Yana da damar da kuke a cikin tabbatar da kuyi rubuta shi kuma kuyi ajiye shi a wuri na aminci wanda ya wuce wurin app na Cake Wallet.",
"pre_seed_button_text": "Ina fahimta. Nuna mini seed din nawa", "pre_seed_button_text": "Ina fahimta. Nuna mini seed din nawa",
"xmr_to_error": "XMR.TO kuskure", "xmr_to_error": "XMR.TO kuskure",
"xmr_to_error_description": "Adadin ba shi da inganci. Maksimum ɗaura 8 digiri bayan decimal point", "xmr_to_error_description": "Adadin ba shi da inganci. Maksimum ɗaura 8 digiri bayan decimal point",
"provider_error": "${provider} kuskure", "provider_error": "${provider} kuskure",
"use_ssl": "Yi amfani da SSL", "use_ssl": "Yi amfani da SSL",
"trusted": "Amintacce", "trusted": "Amintacce",
"color_theme": "Jigon launi", "color_theme": "Jigon launi",
"light_theme": "Haske", "light_theme": "Haske",
"bright_theme": "Mai haske", "bright_theme": "Mai haske",
@ -451,11 +369,9 @@
"transaction_key": "Aikace-aikacen key", "transaction_key": "Aikace-aikacen key",
"confirmations": "Tabbatar", "confirmations": "Tabbatar",
"recipient_address": "Adireshin mai karɓa", "recipient_address": "Adireshin mai karɓa",
"extra_id": "Karin ID:", "extra_id": "Karin ID:",
"destination_tag": "Tambarin makoma:", "destination_tag": "Tambarin makoma:",
"memo": "Memo:", "memo": "Memo:",
"backup": "Ajiyayyen", "backup": "Ajiyayyen",
"change_password": "Canza kalmar shiga", "change_password": "Canza kalmar shiga",
"backup_password": "Ajiyayyen kalmar sirri", "backup_password": "Ajiyayyen kalmar sirri",
@ -463,55 +379,40 @@
"export_backup": "Ajiyayyen fitarwa", "export_backup": "Ajiyayyen fitarwa",
"save_backup_password": "Da fatan za a tabbatar cewa kun adana kalmar sirrin ajiyar ku. Ba za ku iya shigo da fayilolin ajiyar ku ba tare da shi ba.", "save_backup_password": "Da fatan za a tabbatar cewa kun adana kalmar sirrin ajiyar ku. Ba za ku iya shigo da fayilolin ajiyar ku ba tare da shi ba.",
"backup_file": "Ajiyayyen fayil", "backup_file": "Ajiyayyen fayil",
"edit_backup_password": "Shirya Kalmar wucewa ta Ajiyayyen", "edit_backup_password": "Shirya Kalmar wucewa ta Ajiyayyen",
"save_backup_password_alert": "Ajiye kalmar sirri ta ajiya", "save_backup_password_alert": "Ajiye kalmar sirri ta ajiya",
"change_backup_password_alert": "Fayilolin madadin ku na baya ba za su kasance don shigo da sabon kalmar sirri ta madadin ba. Sabuwar kalmar sirri ta ajiya za a yi amfani da ita kawai don sabbin fayilolin madadin. Shin kun tabbata cewa kuna son canza kalmar wucewa?", "change_backup_password_alert": "Fayilolin madadin ku na baya ba za su kasance don shigo da sabon kalmar sirri ta madadin ba. Sabuwar kalmar sirri ta ajiya za a yi amfani da ita kawai don sabbin fayilolin madadin. Shin kun tabbata cewa kuna son canza kalmar wucewa?",
"enter_backup_password": "Shigar da kalmar wucewa ta madadin nan", "enter_backup_password": "Shigar da kalmar wucewa ta madadin nan",
"select_backup_file": "Zaɓi fayil ɗin madadin", "select_backup_file": "Zaɓi fayil ɗin madadin",
"import": "Shigo da", "import": "Shigo da",
"please_select_backup_file": "Da fatan za a zaɓi fayil ɗin madadin kuma shigar da kalmar wucewa ta madadin.", "please_select_backup_file": "Da fatan za a zaɓi fayil ɗin madadin kuma shigar da kalmar wucewa ta madadin.",
"fixed_rate": "Kafaffen ƙima", "fixed_rate": "Kafaffen ƙima",
"fixed_rate_alert": "Za ku iya shigar da adadin karɓa lokacin da aka duba ƙayyadadden zaɓin ƙimar kuɗi. Kuna so ku canza zuwa ƙayyadadden yanayin ƙimar kuɗi?", "fixed_rate_alert": "Za ku iya shigar da adadin karɓa lokacin da aka duba ƙayyadadden zaɓin ƙimar kuɗi. Kuna so ku canza zuwa ƙayyadadden yanayin ƙimar kuɗi?",
"xlm_extra_info": "Don Allah kar a manta da saka Memo ID yayin aika ma'amalar XLM don musayar", "xlm_extra_info": "Don Allah kar a manta da saka Memo ID yayin aika ma'amalar XLM don musayar",
"xrp_extra_info": "Don Allah kar a manta da saka alamar Ƙaddamarwa yayin aika ma'amalar XRP don musayar", "xrp_extra_info": "Don Allah kar a manta da saka alamar Ƙaddamarwa yayin aika ma'amalar XRP don musayar",
"exchange_incorrect_current_wallet_for_xmr": "Idan kana son musanya XMR daga ma'aunin Cake Wallet Monero, da fatan za a fara canza wallet ɗin Monero ɗin ku.", "exchange_incorrect_current_wallet_for_xmr": "Idan kana son musanya XMR daga ma'aunin Cake Wallet Monero, da fatan za a fara canza wallet ɗin Monero ɗin ku.",
"confirmed": "An tabbatar", "confirmed": "An tabbatar",
"unconfirmed": "Ba a tabbatar ba", "unconfirmed": "Ba a tabbatar ba",
"displayable": "Ana iya nunawa", "displayable": "Ana iya nunawa",
"submit_request": "gabatar da bukata", "submit_request": "gabatar da bukata",
"buy_alert_content": "A halin yanzu muna tallafawa kawai siyan Bitcoin da Litecoin. Don siyan Bitcoin ko Litecoin, da fatan za a ƙirƙira ko canza zuwa walat ɗin ku na Bitcoin ko Litecoin.", "buy_alert_content": "A halin yanzu muna tallafawa kawai siyan Bitcoin da Litecoin. Don siyan Bitcoin ko Litecoin, da fatan za a ƙirƙira ko canza zuwa walat ɗin ku na Bitcoin ko Litecoin.",
"sell_alert_content": "A halin yanzu muna tallafawa siyar da Bitcoin da Litecoin kawai. Da fatan za a ƙirƙira ko canza zuwa walat ɗin ku na Bitcoin ko Litecoin.", "sell_alert_content": "A halin yanzu muna tallafawa siyar da Bitcoin da Litecoin kawai. Da fatan za a ƙirƙira ko canza zuwa walat ɗin ku na Bitcoin ko Litecoin.",
"outdated_electrum_wallet_description": "Sabbin walat ɗin Bitcoin da aka kirkira a cikin Cake yanzu suna da nau'in kalma 24. Ya zama dole ka ƙirƙiri sabon walat ɗin Bitcoin kuma canza duk kuɗin ku zuwa sabon walat ɗin kalmomi 24, kuma ku daina amfani da walat tare da iri mai kalma 12. Da fatan za a yi haka nan take don samun kuɗin ku.", "outdated_electrum_wallet_description": "Sabbin walat ɗin Bitcoin da aka kirkira a cikin Cake yanzu suna da nau'in kalma 24. Ya zama dole ka ƙirƙiri sabon walat ɗin Bitcoin kuma canza duk kuɗin ku zuwa sabon walat ɗin kalmomi 24, kuma ku daina amfani da walat tare da iri mai kalma 12. Da fatan za a yi haka nan take don samun kuɗin ku.",
"understand": "na gane", "understand": "na gane",
"apk_update": "apk sabunta", "apk_update": "apk sabunta",
"buy_bitcoin": "Sayi Bitcoin", "buy_bitcoin": "Sayi Bitcoin",
"buy_with": "Saya da", "buy_with": "Saya da",
"moonpay_alert_text": "Darajar adadin dole ne ya zama fiye ko daidai da ${minAmount} ${fiatCurrency}", "moonpay_alert_text": "Darajar adadin dole ne ya zama fiye ko daidai da ${minAmount} ${fiatCurrency}",
"outdated_electrum_wallet_receive_warning": "Idan wannan walat ɗin yana da nau'in kalma 12 kuma an ƙirƙira shi a cikin Cake, KAR KA saka Bitcoin cikin wannan jakar. Duk wani BTC da aka canjawa wuri zuwa wannan walat na iya ɓacewa. Ƙirƙiri sabon walat mai kalmomi 24 (matsa menu a saman dama, zaɓi Wallets, zaɓi Ƙirƙiri Sabon Wallet, sannan zaɓi Bitcoin) kuma NAN nan take matsar da BTC ɗin ku a can. Sabbin (kalmomi 24) BTC wallets daga Cake suna da tsaro", "outdated_electrum_wallet_receive_warning": "Idan wannan walat ɗin yana da nau'in kalma 12 kuma an ƙirƙira shi a cikin Cake, KAR KA saka Bitcoin cikin wannan jakar. Duk wani BTC da aka canjawa wuri zuwa wannan walat na iya ɓacewa. Ƙirƙiri sabon walat mai kalmomi 24 (matsa menu a saman dama, zaɓi Wallets, zaɓi Ƙirƙiri Sabon Wallet, sannan zaɓi Bitcoin) kuma NAN nan take matsar da BTC ɗin ku a can. Sabbin (kalmomi 24) BTC wallets daga Cake suna da tsaro",
"do_not_show_me": "Kar ka sake nuna min wannan", "do_not_show_me": "Kar ka sake nuna min wannan",
"unspent_coins_title": "Tsabar da ba a kashe ba", "unspent_coins_title": "Tsabar da ba a kashe ba",
"unspent_coins_details_title": "Bayanan tsabar kudi da ba a kashe ba", "unspent_coins_details_title": "Bayanan tsabar kudi da ba a kashe ba",
"freeze": "Daskare", "freeze": "Daskare",
"frozen": "Daskararre", "frozen": "Daskararre",
"coin_control": "Sarrafa tsabar kuɗi (na zaɓi)", "coin_control": "Sarrafa tsabar kuɗi (na zaɓi)",
"address_detected": "An gano adireshin", "address_detected": "An gano adireshin",
"address_from_domain": "Wannan adireshin ya fito daga ${domain} akan Unstoppable Domain", "address_from_domain": "Wannan adireshin ya fito daga ${domain} akan Unstoppable Domain",
"add_receiver": "Ƙara wani mai karɓa (na zaɓi)", "add_receiver": "Ƙara wani mai karɓa (na zaɓi)",
"manage_yats": "Sarrafa Yats", "manage_yats": "Sarrafa Yats",
"yat_alert_title": "Aika da karɓar crypto cikin sauƙi tare da Yat", "yat_alert_title": "Aika da karɓar crypto cikin sauƙi tare da Yat",
"yat_alert_content": "Masu amfani da Wallet ɗin Cake yanzu za su iya aikawa da karɓar duk kuɗin da suka fi so tare da sunan mai amfani na tushen emoji iri ɗaya.", "yat_alert_content": "Masu amfani da Wallet ɗin Cake yanzu za su iya aikawa da karɓar duk kuɗin da suka fi so tare da sunan mai amfani na tushen emoji iri ɗaya.",
@ -681,11 +582,10 @@
"contact_list_contacts": "Lambobin sadarwa", "contact_list_contacts": "Lambobin sadarwa",
"contact_list_wallets": "Wallets dina", "contact_list_wallets": "Wallets dina",
"bitcoin_payments_require_1_confirmation": "Akwatin Bitcoin na buɗe 1 sambumbu, da yake za ta samu mintuna 20 ko yawa. Ina kira ga sabuwar lafiya! Zaka sanarwa ta email lokacin da aka samu akwatin samun lambar waya.", "bitcoin_payments_require_1_confirmation": "Akwatin Bitcoin na buɗe 1 sambumbu, da yake za ta samu mintuna 20 ko yawa. Ina kira ga sabuwar lafiya! Zaka sanarwa ta email lokacin da aka samu akwatin samun lambar waya.",
"send_to_this_address" : "Aiko ${currency} ${tag} zuwa adireshin wannan", "send_to_this_address": "Aiko ${currency} ${tag} zuwa adireshin wannan",
"arrive_in_this_address" : "${currency} ${tag} zai je wurin wannan adireshi", "arrive_in_this_address": "${currency} ${tag} zai je wurin wannan adireshi",
"do_not_send": "Kada ka aika", "do_not_send": "Kada ka aika",
"error_dialog_content": "Ai, yanzu muka ga alamar kuskure. \n\nDa fatan, aika rahoton kuskuren da muka kira zuwa gasar tsarinmu don gaskiyar shirya.", "error_dialog_content": "Ai, yanzu muka ga alamar kuskure. \n\nDa fatan, aika rahoton kuskuren da muka kira zuwa gasar tsarinmu don gaskiyar shirya.",
"scan_qr_code": "Gani QR kodin",
"cold_or_recover_wallet": "Samun kashi na baya ko samun kashi na kasa", "cold_or_recover_wallet": "Samun kashi na baya ko samun kashi na kasa",
"please_wait": "Don Allah a rufe", "please_wait": "Don Allah a rufe",
"sweeping_wallet": "Kashi na kasa", "sweeping_wallet": "Kashi na kasa",
@ -705,10 +605,18 @@
"frozen_balance": "Falin kuma maɓallin", "frozen_balance": "Falin kuma maɓallin",
"settings": "Saiti", "settings": "Saiti",
"sell_monero_com_alert_content": "Selling Monero bai sami ƙarshen mai bukatar samun ba", "sell_monero_com_alert_content": "Selling Monero bai sami ƙarshen mai bukatar samun ba",
"error_text_input_below_minimum_limit" : "Kudin ba a kamai", "error_text_input_below_minimum_limit": "Kudin ba a kamai",
"error_text_input_above_maximum_limit" : "Kudin da ya kamata", "error_text_input_above_maximum_limit": "Kudin da ya kamata",
"show_market_place" :"Nuna dan kasuwa", "show_market_place": "Nuna dan kasuwa",
"prevent_screenshots": "Fada lambobi da jarrabobi na kayan lambobi", "prevent_screenshots": "Fada lambobi da jarrabobi na kayan lambobi",
"disable_buy": "Kashe alama", "disable_buy": "Kashe alama",
"disable_sell": "Kashe karbuwa" "disable_sell": "Kashe karbuwa",
"available_balance_description": "Ma'auni mai samuwa” ko ”,Tabbataccen Ma'auni”, kudade ne da za a iya kashewa nan da nan. Idan kudade sun bayyana a cikin ƙananan ma'auni amma ba babban ma'auni ba, to dole ne ku jira 'yan mintoci kaɗan don kudaden shiga don samun ƙarin tabbaci na hanyar sadarwa. Bayan sun sami ƙarin tabbaci, za a kashe su.",
"syncing_wallet_alert_title": "Walat ɗin ku yana aiki tare",
"syncing_wallet_alert_content": "Ma'aunin ku da lissafin ma'amala bazai cika ba har sai an ce \"SYNCHRONIZED\" a saman. Danna/matsa don ƙarin koyo.",
"generate_name": "Ƙirƙirar Suna",
"balance_page": "Ma'auni Page",
"share": "Raba",
"slidable": "Mai iya zamewa"
} }

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

Some files were not shown because too many files have changed in this diff Show more