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:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize"
android:screenOrientation="portrait"
android:exported="true">
<meta-data
android:name="io.flutter.embedding.android.SplashScreenDrawable"

View file

@ -56,9 +56,32 @@ class BitcoinWalletService extends WalletService<
}
@override
Future<void> remove(String wallet) async =>
File(await pathForWalletDir(name: wallet, type: WalletType.bitcoin))
.delete(recursive: true);
Future<void> remove(String wallet) async {
File(await pathForWalletDir(name: wallet, type: getType()))
.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
Future<BitcoinWallet> restoreFromKeys(

View file

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

View file

@ -1,5 +1,6 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'dart:math';
import 'dart:typed_data';
import 'package:cw_bitcoin/encryption_file_utils.dart';
@ -439,6 +440,28 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance,
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
Future<void> changePassword(String password) async {
_password = password;

View file

@ -57,9 +57,32 @@ class LitecoinWalletService extends WalletService<
}
@override
Future<void> remove(String wallet) async =>
File(await pathForWalletDir(name: wallet, type: getType()))
.delete(recursive: true);
Future<void> remove(String wallet) async {
File(await pathForWalletDir(name: wallet, type: getType()))
.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
Future<LitecoinWallet> restoreFromKeys(

View file

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

View file

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

View file

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

View file

@ -1,5 +1,7 @@
import 'dart:async';
import 'dart:io';
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/pathForWallet.dart';
import 'package:cw_core/transaction_priority.dart';
import 'package:cw_haven/haven_transaction_creation_credentials.dart';
import 'package:cw_core/monero_amount_format.dart';
@ -251,6 +253,29 @@ abstract class HavenWalletBase extends WalletBase<MoneroBalance,
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
Future<void> changePassword(String password) async {
haven_wallet.setPasswordSync(password);

View file

@ -149,6 +149,26 @@ class HavenWalletService extends WalletService<
if (isExist) {
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

View file

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

View file

@ -1,4 +1,6 @@
import 'dart:async';
import 'dart:io';
import 'package:cw_core/pathForWallet.dart';
import 'package:cw_core/transaction_priority.dart';
import 'package:cw_core/monero_amount_format.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_core/monero_wallet_utils.dart';
import 'package:cw_monero/api/structs/pending_transaction.dart';
import 'package:flutter/foundation.dart';
import 'package:mobx/mobx.dart';
import 'package:cw_monero/api/transaction_history.dart'
as monero_transaction_history;
@ -274,6 +275,29 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
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
Future<void> changePassword(String password) async {
monero_wallet.setPasswordSync(password);

View file

@ -150,6 +150,26 @@ class MoneroWalletService extends WalletService<
if (isExist) {
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

View file

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

View file

@ -21,4 +21,11 @@ class KeyService {
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';
class WalletLoadingService {
WalletLoadingService(
this.sharedPreferences,
this.keyService,
this.walletServiceFactory);
final SharedPreferences sharedPreferences;
final KeyService keyService;
final WalletService Function(WalletType type) walletServiceFactory;
WalletLoadingService(
this.sharedPreferences, this.keyService, this.walletServiceFactory);
final SharedPreferences sharedPreferences;
final KeyService keyService;
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 {
final walletService = walletServiceFactory.call(type);
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) {
await updateMoneroWalletPassword(wallet);
}
if (type == WalletType.monero) {
await updateMoneroWalletPassword(wallet);
}
return wallet;
}
return wallet;
}
Future<void> updateMoneroWalletPassword(WalletBase wallet) async {
final key = PreferencesKey.moneroWalletUpdateV1Key(wallet.name);
var isPasswordUpdated = sharedPreferences.getBool(key) ?? false;
Future<void> updateMoneroWalletPassword(WalletBase wallet) async {
final key = PreferencesKey.moneroWalletUpdateV1Key(wallet.name);
var isPasswordUpdated = sharedPreferences.getBool(key) ?? false;
if (isPasswordUpdated) {
return;
}
if (isPasswordUpdated) {
return;
}
final password = generateWalletPassword();
// Save new generated password with backup key for case where
// wallet will change password, but it will fail to update in secure storage
final bakWalletName = '#__${wallet.name}_bak__#';
await keyService.saveWalletPassword(walletName: bakWalletName, password: password);
await wallet.changePassword(password);
await keyService.saveWalletPassword(walletName: wallet.name, password: password);
isPasswordUpdated = true;
await sharedPreferences.setBool(key, isPasswordUpdated);
}
final password = generateWalletPassword();
// Save new generated password with backup key for case where
// wallet will change password, but it will fail to update in secure storage
final bakWalletName = '#__${wallet.name}_bak__#';
await keyService.saveWalletPassword(
walletName: bakWalletName, password: password);
await wallet.changePassword(password);
await keyService.saveWalletPassword(
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.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/utils/device_info.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/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_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_verifiable_view_model.dart';
import 'package:cake_wallet/view_model/wallet_unlock_view_model.dart';
@ -582,6 +585,21 @@ Future setup({
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(() {
final wallet = getIt.get<AppStore>().wallet!;

View file

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

View file

@ -194,12 +194,7 @@ class App extends StatefulWidget {
}
class AppState extends State<App> with SingleTickerProviderStateMixin {
AppState() : yatStore = getIt.get<YatStore>() {
SystemChrome.setPreferredOrientations(
ResponsiveLayoutUtil.instance.isIpad ?
[DeviceOrientation.landscapeLeft, DeviceOrientation.landscapeRight] :
[DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]);
}
AppState() : yatStore = getIt.get<YatStore>();
YatStore yatStore;
StreamSubscription? stream;
@ -291,7 +286,43 @@ class AppState extends State<App> with SingleTickerProviderStateMixin {
locale: Locale(settingsStore.languageCode),
onGenerateRoute: (settings) => Router.createRoute(settings),
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/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/wallet_list/wallet_list_item.dart';
import 'package:cake_wallet/wallet_type_utils.dart';
import 'package:flutter/cupertino.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/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/wallet_edit_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/restore/restore_options_page.dart';
@ -275,6 +277,12 @@ Route<dynamic> createRoute(RouteSettings settings) {
return MaterialPageRoute<void>(
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:
return MaterialPageRoute<void>(
fullscreenDialog: true,

View file

@ -11,6 +11,7 @@ class Routes {
static const transactionDetails = '/transaction_info';
static const receive = '/receive';
static const newSubaddress = '/new_subaddress';
static const walletEdit = '/walletEdit';
static const disclaimer = '/disclaimer';
static const readDisclaimer = '/read_disclaimer';
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/store/settings_store.dart';
import 'package:cake_wallet/src/widgets/nav_bar.dart';
import 'package:cake_wallet/generated/i18n.dart';
enum AppBarStyle { regular, withShadow, transparent }
@ -58,7 +59,7 @@ abstract class BasePage extends StatelessWidget {
child: ButtonTheme(
minWidth: double.minPositive,
child: Semantics(
label: 'Back',
label: S.of(context).seed_alert_back,
child: TextButton(
style: ButtonStyle(
overlayColor: MaterialStateColor.resolveWith(

View file

@ -24,35 +24,40 @@ class ContactListPage extends BasePage {
@override
Widget? trailing(BuildContext context) {
return Container(
width: 32.0,
height: 32.0,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Theme.of(context)
return MergeSemantics(
child: Container(
width: 32.0,
height: 32.0,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Theme.of(context)
.accentTextTheme!
.bodySmall!
.color!),
child: Stack(
alignment: Alignment.center,
children: <Widget>[
Icon(Icons.add,
color: Theme.of(context).primaryTextTheme!.titleLarge!.color!,
size: 22.0),
ButtonTheme(
minWidth: 32.0,
height: 32.0,
child: TextButton(
// FIX-ME: Style
//shape: CircleBorder(),
onPressed: () async {
await Navigator.of(context)
.pushNamed(Routes.addressBookAddContact);
},
child: Offstage()),
)
],
));
child: Stack(
alignment: Alignment.center,
children: <Widget>[
Icon(Icons.add,
color: Theme.of(context).primaryTextTheme!.titleLarge!.color!,
size: 22.0),
ButtonTheme(
minWidth: 32.0,
height: 32.0,
child: Semantics(
label: S.of(context).add,
child: TextButton(
// FIX-ME: Style
//shape: CircleBorder(),
onPressed: () async {
await Navigator.of(context)
.pushNamed(Routes.addressBookAddContact);
},
child: Offstage()),
),
)
],
)),
);
}
@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/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/utils/device_info.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/generated/i18n.dart';
@ -42,15 +43,30 @@ class DashboardPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: ResponsiveLayoutUtil.instance.isMobile
? _DashboardPageView(
return Scaffold(body: LayoutBuilder(
builder: (context, constraints) {
if (DeviceInfo.instance.isDesktop) {
if (constraints.maxWidth > ResponsiveLayoutUtil.kDesktopMaxDashBoardWidthConstraint) {
return getIt.get<DesktopSidebarWrapper>();
} else {
return _DashboardPageView(
balancePage: balancePage,
dashboardViewModel: dashboardViewModel,
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,
//padding: EdgeInsets.all(0),
onPressed: () => onOpenEndDrawer(),
child: Semantics(label: 'Menu', child: menuButton)));
child: Semantics(
label: S.of(context).wallet_menu, child: menuButton)));
}
final DashboardViewModel dashboardViewModel;
@ -248,17 +265,17 @@ class _DashboardPageView extends BasePage {
}
if (dashboardViewModel.shouldShowMarketPlaceInDashboard) {
pages.add(Semantics(
label: 'Marketplace Page',
label: S.of(context).market_place,
child: MarketPlacePage(
dashboardViewModel: dashboardViewModel,
dashboardViewModel: dashboardViewModel,
marketPlaceViewModel: getIt.get<MarketPlaceViewModel>(),
),
),
);
}
pages.add(Semantics(label: 'Balance Page', child: balancePage));
pages.add(Semantics(label: S.of(context).balance_page, child: balancePage));
pages.add(Semantics(
label: 'Transactions Page',
label: S.of(context).settings_transactions,
child: TransactionsPage(dashboardViewModel: dashboardViewModel)));
_isEffectsInstalled = true;

View file

@ -14,72 +14,75 @@ class DesktopDashboardActions extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Observer(
builder: (_) {
return Column(
children: [
const SizedBox(height: 16),
DesktopActionButton(
title: MainActions.exchangeAction.name(context),
image: MainActions.exchangeAction.image,
canShow: MainActions.exchangeAction.canShow?.call(dashboardViewModel),
isEnabled: MainActions.exchangeAction.isEnabled?.call(dashboardViewModel),
onTap: () async => await MainActions.exchangeAction.onTap(context, dashboardViewModel),
),
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>(),
return Container(
color: Theme.of(context).colorScheme.background,
child: Observer(
builder: (_) {
return Column(
children: [
const SizedBox(height: 16),
DesktopActionButton(
title: MainActions.exchangeAction.name(context),
image: MainActions.exchangeAction.image,
canShow: MainActions.exchangeAction.canShow?.call(dashboardViewModel),
isEnabled: MainActions.exchangeAction.isEnabled?.call(dashboardViewModel),
onTap: () async => await MainActions.exchangeAction.onTap(context, dashboardViewModel),
),
),
],
);
}
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(
imagePath: 'assets/images/wallet_outline.png',
isSelected: desktopSidebarViewModel.currentPage == SidebarItem.dashboard,
onTap: () => desktopSidebarViewModel.onPageChange(SidebarItem.dashboard),
onTap: () {
desktopSidebarViewModel.onPageChange(SidebarItem.dashboard);
desktopNavigatorKey.currentState
?.pushNamedAndRemoveUntil(Routes.desktop_actions, (route) => false);
},
),
SideMenuItem(
onTap: () {
String? currentPath;
desktopNavigatorKey.currentState?.popUntil((route) {
currentPath = route.settings.name;
return true;
});
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);
});
if (desktopSidebarViewModel.currentPage == SidebarItem.transactions) {
desktopNavigatorKey.currentState
?.pushNamedAndRemoveUntil(Routes.desktop_actions, (route) => false);
desktopSidebarViewModel.resetSidebar();
} else {
desktopSidebarViewModel.onPageChange(SidebarItem.transactions);
desktopNavigatorKey.currentState?.pushNamed(Routes.transactionsPage);
}
},
isSelected: desktopSidebarViewModel.currentPage == SidebarItem.transactions,
@ -156,20 +150,11 @@ class DesktopSidebarWrapper extends BasePage {
void _setEffects() async {
reaction<SidebarItem>((_) => desktopSidebarViewModel.currentPage, (page) {
String? currentPath;
desktopNavigatorKey.currentState?.popUntil((route) {
currentPath = route.settings.name;
return true;
});
if (page == SidebarItem.transactions) {
if (page == SidebarItem.dashboard) {
pageController.jumpToPage(0);
return;
}
if (currentPath == Routes.transactionsPage) {
Navigator.of(desktopNavigatorKey.currentContext!).pop();
}
pageController.jumpToPage(page.index);
pageController.jumpToPage(page.index - 1);
});
}
}

View file

@ -82,7 +82,9 @@ class AddressPage extends BasePage {
child: ButtonTheme(
minWidth: double.minPositive,
child: Semantics(
label: !isMobileView ? 'Close' : 'Back',
label: !isMobileView
? S.of(context).close
: S.of(context).seed_alert_back,
child: TextButton(
style: ButtonStyle(
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/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/show_pop_up.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
@ -14,9 +17,6 @@ class BalancePage extends StatelessWidget {
final DashboardViewModel dashboardViewModel;
final SettingsStore settingsStore;
Color get backgroundLightColor =>
settingsStore.currentTheme.type == ThemeType.bright ? Colors.transparent : Colors.white;
@override
Widget build(BuildContext context) {
return GestureDetector(
@ -44,7 +44,7 @@ class BalancePage extends StatelessWidget {
textAlign: TextAlign.center);
})),
Observer(builder: (_) {
if (dashboardViewModel.balanceViewModel.isShowCard) {
if (dashboardViewModel.balanceViewModel.isShowCard && FeatureFlag.isCakePayEnabled) {
return IntroducingCard(
title: S.of(context).introducing_cake_pay,
subTitle: S.of(context).cake_pay_learn_more,
@ -104,63 +104,86 @@ class BalancePage extends StatelessWidget {
color: Theme.of(context).textTheme!.titleLarge!.backgroundColor!),
child: Container(
margin: const EdgeInsets.only(top: 16, left: 24, right: 24, bottom: 24),
child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
SizedBox(
height: 4,
),
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),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
Expanded(
child: AutoSizeText(availableBalance,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
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(
fontSize: 24,
fontSize: 28,
fontFamily: 'Lato',
fontWeight: FontWeight.w900,
fontWeight: FontWeight.w800,
color: Theme.of(context)
.accentTextTheme!
.displayMedium!
.backgroundColor!,
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,
height: 1)),
],
),
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),
if (frozenBalance.isNotEmpty)
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/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/src/widgets/dashboard_card_widget.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/market_place_view_model.dart';
@ -51,15 +51,17 @@ class MarketPlacePage extends StatelessWidget {
child: ListView(
controller: _scrollController,
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),
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(
Uri.https("buy.cakepay.com"),
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) {
final walletType = dashboardViewModel.type;

View file

@ -138,7 +138,7 @@ class PresentReceiveOptionPicker extends StatelessWidget {
Container(
margin: EdgeInsets.only(bottom: 40),
child: InkWell(
onTap: () => Navigator.pop(context),
onTap: () => Navigator.pop(popUpContext),
child: CircleAvatar(
child: Icon(
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/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/view_model/dashboard/anonpay_transaction_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/sync_status.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
@ -37,6 +39,23 @@ class TransactionsPage extends StatelessWidget {
padding: EdgeInsets.only(top: 24, bottom: 24),
child: Column(
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),
Expanded(child: Observer(builder: (_) {
final items = dashboardViewModel.items;

View file

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

View file

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

View file

@ -30,6 +30,7 @@ class InformationPage extends StatelessWidget {
padding: EdgeInsets.fromLTRB(24, 28, 24, 24),
child: Text(
information,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.normal,
@ -43,7 +44,7 @@ class InformationPage extends StatelessWidget {
padding: EdgeInsets.fromLTRB(10, 0, 10, 10),
child: PrimaryButton(
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!,
textColor: Theme.of(context).primaryTextTheme!.titleLarge!.color!
),

View file

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

View file

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

View file

@ -214,7 +214,7 @@ class IoniaGiftCardDetailPage extends BasePage {
})
.expand((e) => e)
.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
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
Widget middle(BuildContext context) {
return Text(
@ -123,26 +89,28 @@ class IoniaManageCardsPage extends BasePage {
@override
Widget body(BuildContext context) {
final filterButton = InkWell(
onTap: () async {
await showCategoryFilter(context);
_cardsListViewModel.getMerchants();
},
child: Container(
width: 32,
padding: EdgeInsets.all(8),
decoration: BoxDecoration(
color: Theme.of(context).textTheme!.titleLarge!.backgroundColor!,
border: Border.all(
color: Colors.white.withOpacity(0.2),
final filterButton = Semantics(
label: S.of(context).filter_by,
child: InkWell(
onTap: () async {
await showCategoryFilter(context);
_cardsListViewModel.getMerchants();
},
child: Container(
width: 32,
padding: EdgeInsets.all(8),
decoration: BoxDecoration(
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',
color: Theme.of(context).textTheme!.bodySmall!.decorationColor!,
),
)
child: Image.asset(
'assets/images/filter.png',
color: Theme.of(context).textTheme!.bodySmall!.decorationColor!,
),
)),
);
return Padding(
@ -281,11 +249,13 @@ class _SearchWidget extends StatelessWidget {
final FocusNode focusNode;
@override
Widget build(BuildContext context) {
final searchIcon = Padding(
padding: EdgeInsets.all(8),
child: Image.asset(
'assets/images/mini_search_icon.png',
color: Theme.of(context).textTheme!.bodySmall!.decorationColor!,
final searchIcon = ExcludeSemantics(
child: Padding(
padding: EdgeInsets.all(8),
child: Image.asset(
'assets/images/mini_search_icon.png',
color: Theme.of(context).textTheme!.bodySmall!.decorationColor!,
),
),
);
@ -335,18 +305,22 @@ class _TrailingIcon extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Material(
color: Colors.transparent,
child: IconButton(
padding: EdgeInsets.zero,
constraints: BoxConstraints(),
highlightColor: Colors.transparent,
splashColor: Colors.transparent,
iconSize: 25,
onPressed: onPressed,
icon: Image.asset(
asset,
color: Theme.of(context).accentTextTheme!.displayMedium!.backgroundColor!,
return Semantics(
label: S.of(context).profile,
child: Material(
color: Colors.transparent,
child: IconButton(
padding: EdgeInsets.zero,
constraints: BoxConstraints(),
highlightColor: Colors.transparent,
splashColor: Colors.transparent,
iconSize: 25,
onPressed: onPressed,
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/utils/responsive_layout_util.dart';
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:cake_wallet/routes.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/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 {
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);
});
}
MoneroAccountListPage({required this.accountListViewModel});
final MoneroAccountListViewModel accountListViewModel;
ScrollController controller;
double backgroundHeight;
double thumbHeight;
bool isAlwaysShowScrollThumb;
final ScrollController controller = ScrollController();
@override
Widget build(BuildContext context) {
return AlertBackground(
child: Column(
double itemHeight = 80;
double buttonHeight = 62;
return Observer(builder: (_) {
final accounts = accountListViewModel.accounts;
return PickerInnerWrapperWidget(
title: S.of(context).choose_account,
itemsHeight: (itemHeight * accounts.length) + buttonHeight,
children: [
Expanded(
child: Stack(
alignment: Alignment.center,
children: <Widget>[
Column(
child: Scrollbar(
controller: controller,
child: ListView.separated(
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,
children: <Widget>[
Container(
padding: EdgeInsets.only(left: 24, right: 24),
child: Text(
S.of(context).choose_account,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
fontFamily: 'Lato',
decoration: TextDecoration.none,
color: Colors.white
),
),
Icon(
Icons.add,
color: Colors.white,
),
Padding(
padding: EdgeInsets.only(left: 24, right: 24, top: 24),
child: GestureDetector(
onTap: () => null,
child: ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(14)),
child: Container(
height: 296,
color: Theme.of(context).textTheme!.displayLarge!.decorationColor!,
child: Column(
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,
),
),
)
],
),
),
),
)
],
),
),
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';
class AccountTile extends StatelessWidget {
AccountTile({
required this.isCurrent,
required this.accountName,
this.accountBalance,
required this.currency,
required this.onTap,
required this.onEdit
});
AccountTile(
{required this.isCurrent,
required this.accountName,
this.accountBalance,
required this.currency,
required this.onTap,
required this.onEdit});
final bool isCurrent;
final String accountName;
@ -32,11 +31,13 @@ class AccountTile extends StatelessWidget {
height: 77,
padding: EdgeInsets.only(left: 24, right: 24),
color: color,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
child: Wrap(
direction: Axis.horizontal,
alignment: WrapAlignment.spaceBetween,
runAlignment: WrapAlignment.center,
crossAxisAlignment: WrapCrossAlignment.center,
children: [
Expanded(
flex: 2,
Container(
child: Text(
accountName,
style: TextStyle(
@ -49,19 +50,19 @@ class AccountTile extends StatelessWidget {
),
),
if (accountBalance != null)
Expanded(
child: Text(
'${accountBalance.toString()} $currency',
textAlign: TextAlign.end,
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w600,
fontFamily: 'Lato',
color: Theme.of(context).textTheme!.headlineMedium!.color!,
decoration: TextDecoration.none,
Container(
child: Text(
'${accountBalance.toString()} $currency',
textAlign: TextAlign.end,
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w600,
fontFamily: 'Lato',
color: Theme.of(context).textTheme!.headlineMedium!.color!,
decoration: TextDecoration.none,
),
),
),
),
),
],
),
),
@ -80,4 +81,4 @@ class AccountTile extends StatelessWidget {
// onTap: () => onEdit?.call())
// ]);
}
}
}

View file

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

View file

@ -149,7 +149,7 @@ class _WalletNameFormState extends State<WalletNameForm> {
width: 1.0),
),
suffixIcon: Semantics(
label: 'Generate Name',
label: S.of(context).generate_name,
child: IconButton(
onPressed: () async {
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/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/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:cw_core/wallet_type.dart';
import 'package:flutter/material.dart';
class NewWalletTypePage extends BasePage {
NewWalletTypePage({required this.onTypeSelected});
@ -40,92 +41,82 @@ class WalletTypeFormState extends State<WalletTypeForm> {
static const aspectRatioImage = 1.22;
final moneroIcon = Image.asset('assets/images/monero_logo.png', height: 24, width: 24);
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);
final TextEditingController searchController = TextEditingController();
WalletType? selected;
List<WalletType> types;
List<WalletType> filteredTypes = [];
@override
void initState() {
types = availableWalletTypes;
types = filteredTypes = availableWalletTypes;
super.initState();
searchController.addListener(() {
setState(() {
filteredTypes = List.from(types.where((type) => walletTypeToDisplayName(type)
.toLowerCase()
.contains(searchController.text.toLowerCase())));
return;
});
});
}
@override
Widget build(BuildContext context) {
return ScrollableWithBottomSection(
contentPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
content: Center(
return Center(
child: ConstrainedBox(
constraints: BoxConstraints(maxWidth: ResponsiveLayoutUtil.kDesktopMaxWidthConstraint),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Padding(
padding: EdgeInsets.only(left: 12, right: 12),
child: AspectRatio(
aspectRatio: aspectRatioImage,
child: FittedBox(child: widget.walletImage, fit: BoxFit.fill)),
),
Padding(
padding: EdgeInsets.only(top: 48),
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!),
constraints: BoxConstraints(maxWidth: ResponsiveLayoutUtil.kDesktopMaxWidthConstraint),
child: Column(
children: [
Padding(
padding: EdgeInsets.only(top: 48),
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!),
),
),
),
...types.map((type) => Padding(
padding: EdgeInsets.only(top: 24),
child: SelectButton(
image: _iconFor(type),
text: walletTypeToDisplayName(type),
isSelected: selected == type,
onTap: () => setState(() => selected = type)),
))
],
),
),
),
bottomSectionPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
bottomSection: PrimaryButton(
onPressed: () => onTypeSelected(),
text: S.of(context).seed_language_next,
color: Theme.of(context)
.accentTextTheme!
.bodyLarge!
.color!,
textColor: Colors.white,
isDisabled: selected == null,
),
);
}
Image _iconFor(WalletType type) {
switch (type) {
case WalletType.monero:
return moneroIcon;
case WalletType.bitcoin:
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()}');
}
Padding(
padding: const EdgeInsets.fromLTRB(24, 24, 24, 12),
child: SearchBarWidget(searchController: searchController, borderRadius: 24),
),
Expanded(
child: ScrollableWithBottomSection(
contentPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
content: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
...filteredTypes.map((type) => Padding(
padding: EdgeInsets.only(top: 12),
child: SelectButton(
image: Image.asset(
walletTypeToCryptoCurrency(type).iconPath ?? '',
height: 24,
width: 24),
text: walletTypeToDisplayName(type),
showTrailingIcon: false,
height: 54,
isSelected: selected == type,
onTap: () => setState(() => selected = type)),
))
],
),
bottomSectionPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
bottomSection: PrimaryButton(
onPressed: () => onTypeSelected(),
text: S.of(context).seed_language_next,
color: Theme.of(context).accentTextTheme!.bodyLarge!.color!,
textColor: Colors.white,
isDisabled: selected == null,
),
),
),
],
)));
}
Future<void> onTypeSelected() async {

View file

@ -6,12 +6,16 @@ class SelectButton extends StatelessWidget {
required this.onTap,
this.image,
this.isSelected = false,
this.showTrailingIcon = true,
this.height = 60,
});
final Image? image;
final String text;
final bool isSelected;
final VoidCallback onTap;
final bool showTrailingIcon;
final double height;
@override
Widget build(BuildContext context) {
@ -44,7 +48,7 @@ class SelectButton extends StatelessWidget {
onTap: onTap,
child: Container(
width: double.infinity,
height: 60,
height: height,
padding: EdgeInsets.only(left: 30, right: 30),
alignment: Alignment.center,
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/palette.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_two_actions.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cw_core/node.dart';
import 'package:flutter/material.dart';
@ -115,23 +117,42 @@ class NodeCreateOrEditPage extends BasePage {
bottomSectionPadding: EdgeInsets.only(bottom: 24),
bottomSection: Observer(
builder: (_) => Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Flexible(
child: Container(
padding: EdgeInsets.only(right: 8.0),
child: LoadingPrimaryButton(
onPressed: () async {
if (_formKey.currentState != null && !_formKey.currentState!.validate()) {
return;
}
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;
await nodeCreateOrEditViewModel.connect();
if (confirmed) {
await editingNode!.delete();
Navigator.of(context).pop();
}
},
isLoading: nodeCreateOrEditViewModel
.connectionState is IsExecutingState,
text: S.of(context).node_test,
isDisabled: !nodeCreateOrEditViewModel.isReady,
color: Colors.orange,
text: S.of(context).delete,
isDisabled: !nodeCreateOrEditViewModel.isReady ||
(isSelected ?? false),
color: Palette.red,
textColor: Colors.white),
)),
Flexible(

View file

@ -9,8 +9,8 @@ class NodeIndicator extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
width: 8.0,
height: 8.0,
width: 12.0,
height: 12.0,
decoration: BoxDecoration(
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/widgets/standard_list.dart';
import 'package:cw_core/node.dart';
import 'package:flutter/material.dart';
class NodeListRow extends StandardListRow {
NodeListRow(
{required String title,
required this.node,
required void Function(BuildContext context) onTap,
required bool isSelected,
required this.isAlive})
required bool isSelected})
: super(title: title, onTap: onTap, isSelected: isSelected);
final Future<bool> isAlive;
final Node node;
@override
Widget buildTrailing(BuildContext context) {
Widget buildLeading(BuildContext context) {
return FutureBuilder(
future: isAlive,
future: node.requestNode(),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
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 {

View file

@ -226,7 +226,7 @@ class PinCodeState<T extends PinCodeWidget> extends State<T> {
child: Container(
margin: EdgeInsets.only(left: marginLeft, right: marginRight),
child: Semantics(
label: 'Delete',
label: S.of(context).delete,
button: true,
onTap: () => _pop(),
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/widgets/alert_with_one_action.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/view_model/anon_invoice_page_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;
@override
void onClose(BuildContext context) {
Navigator.popUntil(context, ModalRoute.withName(Routes.dashboard));
}
void onClose(BuildContext context) => Navigator.popUntil(context, (route) => route.isFirst);
@override
Widget middle(BuildContext context) =>

View file

@ -32,28 +32,7 @@ class AnonPayReceivePage extends BasePage {
bool get resizeToAvoidBottomInset => false;
@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(
onPressed: () =>
Navigator.pushNamedAndRemoveUntil(context, Routes.dashboard, (route) => false),
child: _backButton),
),
);
}
void onClose(BuildContext context) => Navigator.popUntil(context, (route) => route.isFirst);
@override
Widget middle(BuildContext context) {

View file

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

View file

@ -71,7 +71,7 @@ class AddressCell extends StatelessWidget {
),
));
return Semantics(
label: 'Slidable',
label: S.of(context).slidable,
selected: isCurrent,
enabled: !isCurrent,
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:cake_wallet/routes.dart';
import 'package:cake_wallet/themes/theme_base.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/widgets/primary_button.dart';
@ -37,46 +36,33 @@ class PreSeedPage extends BasePage {
alignment: Alignment.center,
padding: EdgeInsets.all(24),
child: ConstrainedBox(
constraints: BoxConstraints(maxWidth: ResponsiveLayoutUtil.kDesktopMaxWidthConstraint),
constraints: BoxConstraints(maxWidth: ResponsiveLayoutUtil.kDesktopMaxWidthConstraint),
child: Column(
children: [
Flexible(
flex: 2,
child: AspectRatio(
aspectRatio: 1,
child: FittedBox(child: image, fit: BoxFit.contain))),
Flexible(
flex: 3,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Padding(
padding: EdgeInsets.only(top: 70, left: 16, right: 16),
child: Text(
S
.of(context)
.pre_seed_description(wordsCount.toString()),
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.normal,
color: Theme.of(context)
.primaryTextTheme!
.bodySmall!
.color!),
),
),
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)
],
))
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
ConstrainedBox(
constraints: BoxConstraints(
maxHeight: MediaQuery.of(context).size.height * 0.3
),
child: AspectRatio(aspectRatio: 1, child: image),
),
Padding(
padding: EdgeInsets.all(10),
child: Text(
S.of(context).pre_seed_description(wordsCount.toString()),
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.normal,
color: Theme.of(context).primaryTextTheme.bodySmall!.color!),
),
),
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
Widget? leading(BuildContext context) =>
isNewWalletCreated ? null: super.leading(context);
Widget? leading(BuildContext context) => isNewWalletCreated ? null : super.leading(context);
@override
Widget trailing(BuildContext context) {
@ -67,16 +66,11 @@ class WalletSeedPage extends BasePage {
margin: EdgeInsets.only(left: 10),
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(16)),
color: Theme.of(context)
.accentTextTheme!
.bodySmall!
.color!),
color: Theme.of(context).accentTextTheme.bodySmall!.color!),
child: Text(
S.of(context).seed_language_next,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: Palette.blueCraiola),
fontSize: 14, fontWeight: FontWeight.w600, color: Palette.blueCraiola),
),
),
)
@ -87,121 +81,99 @@ class WalletSeedPage extends BasePage {
Widget body(BuildContext context) {
final image = currentTheme.type == ThemeType.dark ? imageDark : imageLight;
return WillPopScope(onWillPop: () async => false, child: Container(
padding: EdgeInsets.all(24),
alignment: Alignment.center,
child: ConstrainedBox(
constraints:
BoxConstraints(maxWidth: ResponsiveLayoutUtil.kDesktopMaxWidthConstraint),
child: Column(
children: <Widget>[
Flexible(
flex: 2,
child: AspectRatio(
aspectRatio: 1,
child: FittedBox(child: image, fit: BoxFit.fill))),
Flexible(
flex: 3,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
return WillPopScope(
onWillPop: () async => false,
child: Container(
padding: EdgeInsets.all(24),
alignment: Alignment.center,
child: ConstrainedBox(
constraints: BoxConstraints(maxWidth: ResponsiveLayoutUtil.kDesktopMaxWidthConstraint),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
ConstrainedBox(
constraints:
BoxConstraints(maxHeight: MediaQuery.of(context).size.height * 0.3),
child: AspectRatio(aspectRatio: 1, child: image),
),
Observer(builder: (_) {
return Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Padding(
padding: EdgeInsets.only(top: 33),
child: Observer(builder: (_) {
return Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
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!),
),
)
],
);
}),
Text(
walletSeedViewModel.name,
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w600,
color: Theme.of(context).primaryTextTheme.titleLarge!.color!),
),
Column(
children: <Widget>[
isNewWalletCreated
? Padding(
padding: EdgeInsets.only(
bottom: 52, 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)),
))
],
)
],
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(
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(
minWidth: double.minPositive,
child: Semantics(
label: !isMobileView ? 'Close' : 'Back',
label: !isMobileView
? S.of(context).close
: S.of(context).seed_alert_back,
child: TextButton(
style: ButtonStyle(
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/alert_with_two_actions.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 {
ConnectionSyncPage(this.nodeListViewModel, this.dashboardViewModel);
@ -64,49 +63,37 @@ class ConnectionSyncPage extends BasePage {
itemBuilder: (_, sectionIndex, index) {
final node = nodeListViewModel.nodes[index];
final isSelected = node.keyIndex == nodeListViewModel.currentNode.keyIndex;
final nodeListRow = Semantics(
label: 'Slidable',
selected: isSelected,
enabled: !isSelected,
child: NodeListRow(
title: node.uriRaw,
isSelected: isSelected,
isAlive: node.requestNode(),
onTap: (_) async {
if (isSelected) {
return;
}
final nodeListRow = NodeListRow(
title: node.uriRaw,
node: node,
isSelected: isSelected,
onTap: (_) async {
if (isSelected) {
return;
}
await showPopUp<void>(
context: context,
builder: (BuildContext context) {
return AlertWithTwoActions(
alertTitle:
S.of(context).change_current_node_title,
alertContent: nodeListViewModel
.getAlertContent(node.uriRaw),
leftButtonText: S.of(context).cancel,
rightButtonText: S.of(context).change,
actionLeftButton: () =>
Navigator.of(context).pop(),
actionRightButton: () async {
await nodeListViewModel.setAsCurrent(node);
Navigator.of(context).pop();
},
);
});
},
),
await showPopUp<void>(
context: context,
builder: (BuildContext context) {
return AlertWithTwoActions(
alertTitle:
S.of(context).change_current_node_title,
alertContent: nodeListViewModel
.getAlertContent(node.uriRaw),
leftButtonText: S.of(context).cancel,
rightButtonText: S.of(context).change,
actionLeftButton: () =>
Navigator.of(context).pop(),
actionRightButton: () async {
await nodeListViewModel.setAsCurrent(node);
Navigator.of(context).pop();
},
);
});
},
);
final dismissibleRow = Slidable(
key: Key('${node.keyIndex}'),
startActionPane: _actionPane(context, node, isSelected),
endActionPane: _actionPane(context, node, isSelected),
child: nodeListRow,
);
return dismissibleRow;
return nodeListRow;
},
),
);
@ -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:another_flushbar/flushbar.dart';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:cake_wallet/routes.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/screens/base_page.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';
class WalletListPage extends BasePage {
@ -79,31 +77,7 @@ class WalletListBodyState extends State<WalletListBody> {
? Theme.of(context).accentTextTheme!.titleSmall!.decorationColor!
: Theme.of(context).colorScheme.background;
final row = GestureDetector(
onTap: () async {
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);
}
},
onTap: () => wallet.isCurrent ? null : _loadWallet(wallet),
child: Container(
height: tileHeight,
width: double.infinity,
@ -131,16 +105,21 @@ class WalletListBodyState extends State<WalletListBody> {
? _imageFor(type: wallet.type)
: nonWalletTypeIcon,
SizedBox(width: 10),
Text(
wallet.name,
style: TextStyle(
Flexible(
child: Text(
wallet.name,
maxLines: null,
softWrap: true,
style: TextStyle(
fontSize: 22,
fontWeight: FontWeight.w500,
color: Theme.of(context)
.primaryTextTheme
.titleLarge!
.color!),
)
.color!,
),
),
),
],
),
),
@ -151,12 +130,38 @@ class WalletListBodyState extends State<WalletListBody> {
return wallet.isCurrent
? row
: Slidable(
key: Key('${wallet.key}'),
startActionPane: _actionPane(wallet),
endActionPane: _actionPane(wallet),
child: row,
);
: Row(children: [
Expanded(child: row),
GestureDetector(
onTap: () => Navigator.of(context).pushNamed(
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();
// only pop the wallets route in mobile as it will go back to dashboard page
// in desktop platforms the navigation tree is different
if (ResponsiveLayoutUtil.instance.isMobile) {
if (ResponsiveLayoutUtil.instance.shouldRenderMobileUI()) {
WidgetsBinding.instance.addPostFrameCallback((_) {
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) {
_progressBar = createBar<void>(text, duration: null)..show(context);
}
@ -279,18 +243,4 @@ class WalletListBodyState extends State<WalletListBody> {
_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
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',
height: 12,
width: 12,
color: Theme.of(context)
.accentTextTheme!
.headlineSmall!
.accentTextTheme.headlineSmall!
.decorationColor!);
final restoreWalletImage = Image.asset('assets/images/restore_wallet.png',
height: 12,
width: 12,
color: Theme.of(context).primaryTextTheme!.titleLarge!.color!);
color: Theme.of(context).primaryTextTheme.titleLarge!.color!);
return WillPopScope(
onWillPop: () async => false,
child: Container(
alignment: Alignment.center,
padding: EdgeInsets.only(top: 64, bottom: 24, left: 24, right: 24),
child: Center(
child: ConstrainedBox(
constraints:
BoxConstraints(maxWidth: ResponsiveLayoutUtil.kDesktopMaxWidthConstraint),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Flexible(
flex: 2,
child: AspectRatio(
aspectRatio: aspectRatioImage,
child: FittedBox(child: welcomeImage, fit: BoxFit.fill))),
Flexible(
flex: 3,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Column(
children: <Widget>[
Padding(
padding: EdgeInsets.only(top: 24),
child: Text(
S.of(context).welcome,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w500,
color: Theme.of(context)
.accentTextTheme!
.displayMedium!
.color,
),
textAlign: TextAlign.center,
),
),
Padding(
padding: EdgeInsets.only(top: 5),
child: Text(
appTitle(context),
style: TextStyle(
fontSize: 36,
fontWeight: FontWeight.bold,
color: Theme.of(context)
.primaryTextTheme!
.titleLarge!
.color!,
),
textAlign: TextAlign.center,
),
),
Padding(
padding: EdgeInsets.only(top: 5),
child: Text(
appDescription(context),
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
color: Theme.of(context)
.accentTextTheme!
.displayMedium!
.color,
),
textAlign: TextAlign.center,
),
),
],
),
Column(
children: <Widget>[
Text(
S.of(context).please_make_selection,
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.normal,
color: Theme.of(context)
.accentTextTheme!
.displayMedium!
.color,
),
textAlign: TextAlign.center,
),
Padding(
padding: EdgeInsets.only(top: 24),
child: PrimaryImageButton(
onPressed: () =>
Navigator.pushNamed(context, Routes.newWalletFromWelcome),
image: newWalletImage,
text: S.of(context).create_new,
color: Theme.of(context)
.accentTextTheme!
.titleSmall!
.decorationColor!,
textColor: Theme.of(context)
.accentTextTheme!
.headlineSmall!
.decorationColor!,
),
),
Padding(
padding: EdgeInsets.only(top: 10),
child: PrimaryImageButton(
onPressed: () {
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!),
)
],
)
],
))
],
),
child: ConstrainedBox(
constraints: BoxConstraints(
maxWidth: ResponsiveLayoutUtil.kDesktopMaxWidthConstraint),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Column(
children: <Widget>[
AspectRatio(
aspectRatio: aspectRatioImage,
child: FittedBox(
child: welcomeImage, fit: BoxFit.contain),
),
Padding(
padding: EdgeInsets.only(top: 24),
child: Text(
S.of(context).welcome,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w500,
color: Theme.of(context)
.accentTextTheme.displayMedium!
.color!,
),
textAlign: TextAlign.center,
),
),
Padding(
padding: EdgeInsets.only(top: 5),
child: Text(
appTitle(context),
style: TextStyle(
fontSize: 36,
fontWeight: FontWeight.bold,
color: Theme.of(context)
.primaryTextTheme.titleLarge!
.color!,
),
textAlign: TextAlign.center,
),
),
Padding(
padding: EdgeInsets.only(top: 5),
child: Text(
appDescription(context),
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
color: Theme.of(context)
.accentTextTheme.displayMedium!
.color!,
),
textAlign: TextAlign.center,
),
),
],
),
Column(
children: <Widget>[
Text(
S.of(context).please_make_selection,
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.normal,
color: Theme.of(context)
.accentTextTheme.displayMedium!
.color!,
),
textAlign: TextAlign.center,
),
Padding(
padding: EdgeInsets.only(top: 24),
child: PrimaryImageButton(
onPressed: () => Navigator.pushNamed(
context, Routes.newWalletFromWelcome),
image: newWalletImage,
text: S.of(context).create_new,
color: Theme.of(context)
.accentTextTheme.titleSmall!
.decorationColor!,
textColor: Theme.of(context)
.accentTextTheme.headlineSmall!
.decorationColor!,
),
),
Padding(
padding: EdgeInsets.only(top: 10),
child: PrimaryImageButton(
onPressed: () {
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,
height: prefixIconHeight,
padding: EdgeInsets.only(top: 0),
child: InkWell(
onTap: () async => _pasteAddress(context),
child: Container(
padding: EdgeInsets.all(8),
decoration: BoxDecoration(
color: buttonColor ??
Theme.of(context)
.accentTextTheme!
child: Semantics(
label: S.of(context).paste,
child: InkWell(
onTap: () async => _pasteAddress(context),
child: Container(
padding: EdgeInsets.all(8),
decoration: BoxDecoration(
color: buttonColor ??
Theme.of(context)
.accentTextTheme
!
.titleLarge!
.color!,
borderRadius:
BorderRadius.all(Radius.circular(6))),
child: Image.asset(
'assets/images/paste_ios.png',
color: iconColor ??
Theme.of(context)
.primaryTextTheme!
.color!,
borderRadius:
BorderRadius.all(Radius.circular(6))),
child: Image.asset(
'assets/images/paste_ios.png',
color: iconColor ??
Theme.of(context)
.primaryTextTheme
!
.headlineMedium!
.decorationColor!,
)),
.decorationColor!,
)),
),
)),
],
if (this.options.contains(AddressTextFieldOption.qrCode) && DeviceInfo.instance.isMobile)
...[
if (this.options.contains(AddressTextFieldOption.qrCode) &&
DeviceInfo.instance.isMobile) ...[
Container(
width: prefixIconWidth,
height: prefixIconHeight,
padding: EdgeInsets.only(top: 0),
child: InkWell(
onTap: () async => _presentQRScanner(context),
child: Container(
padding: EdgeInsets.all(8),
decoration: BoxDecoration(
color: buttonColor ??
Theme.of(context)
.accentTextTheme
child: Semantics(
label: S.of(context).scan_qr_code,
child: InkWell(
onTap: () async => _presentQRScanner(context),
child: Container(
padding: EdgeInsets.all(8),
decoration: BoxDecoration(
color: buttonColor ??
Theme.of(context)
.accentTextTheme
.titleLarge!
.color!,
borderRadius:
BorderRadius.all(Radius.circular(6))),
child: Image.asset(
'assets/images/qr_code_icon.png',
color: iconColor ??
Theme.of(context)
.primaryTextTheme!.headlineMedium!
.decorationColor!,
)),
.color!,
borderRadius:
BorderRadius.all(Radius.circular(6))),
child: Image.asset(
'assets/images/qr_code_icon.png',
color: iconColor ??
Theme.of(context)
.primaryTextTheme
!.headlineMedium!
.decorationColor!,
)),
),
))
] else SizedBox(width: 5),
] else
SizedBox(width: 5),
if (this
.options
.contains(AddressTextFieldOption.addressBook)) ...[
@ -170,26 +181,32 @@ class AddressTextField extends StatelessWidget {
width: prefixIconWidth,
height: prefixIconHeight,
padding: EdgeInsets.only(top: 0),
child: InkWell(
onTap: () async => _presetAddressBookPicker(context),
child: Container(
padding: EdgeInsets.all(8),
decoration: BoxDecoration(
color: buttonColor ??
Theme.of(context)
.accentTextTheme!
child: Semantics(
label: S.of(context).address_book,
child: InkWell(
onTap: () async =>
_presetAddressBookPicker(context),
child: Container(
padding: EdgeInsets.all(8),
decoration: BoxDecoration(
color: buttonColor ??
Theme.of(context)
.accentTextTheme
!
.titleLarge!
.color!,
borderRadius:
BorderRadius.all(Radius.circular(6))),
child: Image.asset(
'assets/images/open_book.png',
color: iconColor ??
Theme.of(context)
.primaryTextTheme!
.color!,
borderRadius:
BorderRadius.all(Radius.circular(6))),
child: Image.asset(
'assets/images/open_book.png',
color: iconColor ??
Theme.of(context)
.primaryTextTheme
!
.headlineMedium!
.decorationColor!,
)),
.decorationColor!,
)),
),
))
]
],

View file

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

View file

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

View file

@ -1,5 +1,6 @@
// 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:flutter/material.dart';
import 'package:cw_core/currency.dart';
@ -158,37 +159,7 @@ class _PickerState<Item> extends State<Picker<Item>> {
if (widget.hintText != null)
Padding(
padding: const EdgeInsets.all(16),
child: TextFormField(
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,
)),
),
),
child: SearchBarWidget(searchController: searchController),
),
Divider(
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 StandardListSeparator(padding: EdgeInsets.only(left: 24));
return StandardListSeparator(padding: dividerPadding);
},
itemCount: totalRows.length,
itemBuilder: (_, index) => totalRows[index]);

View file

@ -146,15 +146,13 @@ abstract class SettingsStoreBase with Store {
});
}
reaction(
(_) => disableBuy,
(bool disableBuy) => sharedPreferences.setBool(
PreferencesKey.disableBuyKey, disableBuy));
reaction((_) => disableBuy,
(bool disableBuy) => sharedPreferences.setBool(PreferencesKey.disableBuyKey, disableBuy));
reaction(
(_) => disableSell,
(bool disableSell) => sharedPreferences.setBool(
PreferencesKey.disableSellKey, disableSell));
(bool disableSell) =>
sharedPreferences.setBool(PreferencesKey.disableSellKey, disableSell));
reaction(
(_) => fiatApiMode,
@ -352,12 +350,9 @@ abstract class SettingsStoreBase with Store {
// FIX-ME: Check for which default value we should have here
final shouldSaveRecipientAddress =
sharedPreferences.getBool(PreferencesKey.shouldSaveRecipientAddressKey) ?? false;
final isAppSecure =
sharedPreferences.getBool(PreferencesKey.isAppSecureKey) ?? false;
final disableBuy =
sharedPreferences.getBool(PreferencesKey.disableBuyKey) ?? false;
final disableSell =
sharedPreferences.getBool(PreferencesKey.disableSellKey) ?? false;
final isAppSecure = sharedPreferences.getBool(PreferencesKey.isAppSecureKey) ?? false;
final disableBuy = sharedPreferences.getBool(PreferencesKey.disableBuyKey) ?? false;
final disableSell = sharedPreferences.getBool(PreferencesKey.disableSellKey) ?? false;
final currentFiatApiMode = FiatApiMode.deserialize(
raw: sharedPreferences.getInt(PreferencesKey.currentFiatApiModeKey) ??
FiatApiMode.enabled.raw);
@ -494,16 +489,14 @@ abstract class SettingsStoreBase with Store {
useTOTP2FA = sharedPreferences.getBool(PreferencesKey.useTOTP2FA) ?? useTOTP2FA;
numberOfFailedTokenTrials =
sharedPreferences.getInt(PreferencesKey.failedTotpTokenTrials) ?? numberOfFailedTokenTrials;
sharedPreferences.getBool(PreferencesKey.shouldSaveRecipientAddressKey) ?? shouldSaveRecipientAddress;
isAppSecure =
sharedPreferences.getBool(PreferencesKey.isAppSecureKey) ?? isAppSecure;
disableBuy =
sharedPreferences.getBool(PreferencesKey.disableBuyKey) ?? disableBuy;
disableSell =
sharedPreferences.getBool(PreferencesKey.disableSellKey) ?? disableSell;
allowBiometricalAuthentication = sharedPreferences
.getBool(PreferencesKey.allowBiometricalAuthenticationKey) ??
allowBiometricalAuthentication;
sharedPreferences.getBool(PreferencesKey.shouldSaveRecipientAddressKey) ??
shouldSaveRecipientAddress;
isAppSecure = sharedPreferences.getBool(PreferencesKey.isAppSecureKey) ?? isAppSecure;
disableBuy = sharedPreferences.getBool(PreferencesKey.disableBuyKey) ?? disableBuy;
disableSell = sharedPreferences.getBool(PreferencesKey.disableSellKey) ?? disableSell;
allowBiometricalAuthentication =
sharedPreferences.getBool(PreferencesKey.allowBiometricalAuthenticationKey) ??
allowBiometricalAuthentication;
shouldShowMarketPlaceInDashboard =
sharedPreferences.getBool(PreferencesKey.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';
class ResponsiveLayoutUtil {
static const double _kMobileThreshold = 768;
static const double _kMobileThreshold = 550;
static const double kDesktopMaxWidthConstraint = 400;
static const double kDesktopMaxDashBoardWidthConstraint = 900;
static const double kPopupWidth = 400;
static const double kPopupSpaceHeight = 100;
static const _kIpadMaxWidth = 2560.0;
const ResponsiveLayoutUtil._();
static final instance = ResponsiveLayoutUtil._();
bool get isMobile =>
MediaQueryData.fromWindow(WidgetsBinding.instance.window).size.width < _kMobileThreshold;
MediaQueryData.fromWindow(WidgetsBinding.instance.window).size.shortestSide <=
_kMobileThreshold;
bool get isIpad {
final width = MediaQueryData.fromWindow(WidgetsBinding.instance.window).size.width;
return width >= _kMobileThreshold && !(width > _kIpadMaxWidth);
bool shouldRenderMobileUI() {
final mediaQuery = MediaQueryData.fromWindow(WidgetsBinding.instance.window);
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.

View file

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

View file

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

View file

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

View file

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

View file

@ -40,7 +40,7 @@ abstract class TradeDetailsViewModelBase with Store {
_provider = XMRTOExchangeProvider();
break;
case ExchangeProviderDescription.changeNow:
_provider = ChangeNowExchangeProvider();
_provider = ChangeNowExchangeProvider(settingsStore: settingsStore);
break;
case ExchangeProviderDescription.morphToken:
_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/wallet_loading_service.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/balance.dart';
import 'package:cw_core/transaction_history.dart';
import 'package:cw_core/transaction_info.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:hive/hive.dart';
import 'package:mobx/mobx.dart';
import 'package:cake_wallet/di.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:cw_core/wallet_info.dart';
import 'package:cw_core/wallet_type.dart';
@ -25,8 +24,8 @@ abstract class WalletListViewModelBase with Store {
this._walletLoadingService,
this._authService,
) : wallets = ObservableList<WalletListItem>() {
_updateList();
reaction((_) => _appStore.wallet, (_) => _updateList());
updateList();
reaction((_) => _appStore.wallet, (_) => updateList());
}
@observable
@ -41,20 +40,14 @@ abstract class WalletListViewModelBase with Store {
@action
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);
_updateList();
}
@action
Future<void> remove(WalletListItem wallet) async {
final walletService = getIt.get<WalletService>(param1: wallet.type);
await walletService.remove(wallet.name);
await _walletInfoSource.delete(wallet.key);
_updateList();
}
void _updateList() {
void updateList() {
wallets.clear();
wallets.addAll(
_walletInfoSource.values.map(
@ -62,7 +55,8 @@ abstract class WalletListViewModelBase with Store {
name: info.name,
type: info.type,
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),
),
),

View file

@ -115,7 +115,7 @@ SPEC CHECKSUMS:
platform_device_id_macos: f763bb55f088be804d61b96eb4710b8ab6598e94
ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825
share_plus_macos: 853ee48e7dce06b633998ca0735d482dd671ade4
shared_preferences_foundation: e2dae3258e06f44cc55f49d42024fd8dd03c590c
shared_preferences_foundation: 5b919d13b803cadd15ed2dc053125c68730e5126
url_launcher_macos: 5335912b679c073563f29d89d33d10d459f95451
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.",
"create_new": "Ƙirƙira Sabon Kwalinku",
"restore_wallet": "Dawo da Kwalinku",
"monero_com": "Monero.com ta Cake Wallet",
"monero_com_wallet_text": "Aikace-aikacen e-wallet ga Monero",
"haven_app": "Haven da Cake Wallet",
"haven_app_wallet_text": "Aikace-aikacen e-wallet ga Haven",
"accounts": "Lissafi",
"edit": "Gyara",
"account": "Asusu",
"add": "Ƙara",
"address_book": "Littafin adireshi",
"contact": "Tuntuɓar",
"please_select": "Don Allah zaɓi:",
@ -28,13 +23,9 @@
"save": "Ajiye",
"address_remove_contact": "Cire lamba",
"address_remove_content": "Kuna tabbatar kuna so ku cire wannan Contact?",
"authenticated": "Ingantacce",
"authentication": "Tabbatarwa",
"failed_authentication": "Binne wajen shiga. ${state_error}",
"wallet_menu": "Menu",
"Blocks_remaining": "${status} Katanga ya rage",
"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",
"reconnection": "Sake haɗawa",
"reconnect_alert_text": "Shin kun tabbata kuna son sake haɗawa?",
"exchange": "Exchange",
"clear": "Share",
"refund_address": "Adireshin maidowa",
@ -80,7 +69,6 @@
"change_currency": "Canja Kuɗi",
"overwrite_amount": "Rubuta adadin",
"qr_payment_amount": "Wannan QR code yana da adadin kuɗi. Kuna so ku overwrite wannan adadi?",
"copy_id": "Kwafi ID",
"exchange_result_write_down_trade_id": "Da fatan za a kwafa ko rubuta ID ɗin ciniki don ci gaba.",
"trade_id": "ID na kasuwanci:",
@ -106,20 +94,13 @@
"time": "${minutes}m ${seconds}s",
"send_xmr": "Aika XMR",
"exchange_new_template": "Sabon template",
"faq": "FAQ",
"enter_your_pin": "Shigar da PIN",
"loading_your_wallet": "Ana loda walat ɗin ku",
"new_wallet": "Sabuwar Wallet",
"wallet_name": "Sunan walat",
"continue_text": "Ci gaba",
"choose_wallet_currency": "Da fatan za a zaɓi kuɗin walat:",
"node_new": "Sabon Node",
"node_address": "Address Node",
"node_port": "Node tashar jiragen ruwa",
@ -140,24 +121,18 @@
"node_connection_successful": "Haɗin ya yi nasara",
"node_connection_failed": "Haɗin ya gaza",
"new_node_testing": "Sabbin gwajin kumburi",
"use": "Canja zuwa",
"digit_pin": "-lambar PIN",
"share_address": "Raba adireshin",
"receive_amount": "Adadi",
"subaddresses": "Subaddresses",
"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",
"rename": "Sake suna",
"choose_account": "Zaɓi asusu",
"create_new_account": "Ƙirƙiri sabon asusu",
"accounts_subaddresses": "Accounts da subaddresses",
"restore_restore_wallet": "Maida Wallet",
"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",
@ -181,14 +156,10 @@
"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_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_title": "iri",
"seed_share": "Raba iri",
"copy": "Kwafi",
"seed_language_choose": "Don Allah zaɓi harshen seed:",
"seed_choose": "Zaɓi harshen seed",
"seed_language_next": "Na gaba",
@ -202,8 +173,6 @@
"seed_language_spanish": "Spanish",
"seed_language_french": "Faransanci",
"seed_language_italian": "Italiyanci",
"send_title": "Aika",
"send_your_wallet": "Walat ɗin ku",
"send_address": "${cryptoCurrency} address",
@ -219,11 +188,9 @@
"send_amount": "Adadi:",
"send_fee": "Kudin:",
"send_name": "Sunan",
"send_got_it": "Gama",
"got_it": "Gama",
"send_sending": "Aika...",
"send_success": "${crypto} kwalinku ya aika da nasara",
"settings_title": "Saitunan",
"settings_nodes": "Nodes",
"settings_current_node": "Node yanzu",
@ -247,13 +214,9 @@
"settings_support": "Taimako",
"settings_terms_and_conditions": "Sharuɗɗa da Ka'idoji",
"pin_is_incorrect": "PIN ba daidai ba ne",
"setup_pin": "Saita PIN",
"enter_your_pin_again": "Shigar da PIN ɗinku na sake",
"setup_successful": "An saita PIN ɗinku da nasara!",
"wallet_keys": "Iri/maɓalli na walat",
"wallet_seed": "kalmar sirri na walat",
"private_key": "Keɓaɓɓen maɓalli",
@ -263,17 +226,11 @@
"spend_key_private": "makullin biya (maɓallin kalmar sirri)",
"spend_key_public": "makullin biya (maɓallin jama'a)",
"copied_key_to_clipboard": "An kwafa ${key} a cikin kwafin",
"new_subaddress_title": "Adireshin sabuwa",
"new_subaddress_label_name": "Lakabin suna",
"new_subaddress_create": "Ƙirƙiri",
"address_label": "Labari adireshi",
"subaddress_title": "Jagorar subaddress",
"trade_details_title": "Bayanai game da kasuwancin",
"trade_details_id": "ID",
"trade_details_state": "Matsayi",
@ -282,11 +239,7 @@
"trade_details_created_at": "An ƙirƙira a",
"trade_details_pair": "miji da matarsa",
"trade_details_copied": "${title} an kwafa zuwa cikin kwafin",
"trade_history_title": "Tarihin kasuwancin",
"transaction_details_title": "Bayanai game da aikace-aikacen",
"transaction_details_transaction_id": "ID na kasuwanci",
"transaction_details_date": "Kwanan wata",
@ -295,28 +248,22 @@
"transaction_details_fee": "Kudin",
"transaction_details_copied": "${title} an kwafa zuwa cikin kwafin",
"transaction_details_recipient_address": "Adireshin masu amfani",
"wallet_list_title": "Monero walat",
"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_load_wallet": "Ana loda wallet na Monero",
"wallet_list_loading_wallet": "Ana loda ${wallet_name} walat",
"wallet_list_failed_to_load": "An kasa loda ${wallet_name} walat. ${error}",
"wallet_list_removing_wallet": "Cirewa ${wallet_name} walat",
"wallet_list_failed_to_remove": "Ba a iya cirewa ${wallet_name} walat. ${error}",
"widgets_address": "Adireshin",
"widgets_restore_from_blockheight": "Sake dawo da daga blockheight",
"widgets_restore_from_date": "Sake dawo da daga kwanan wata",
"widgets_or": "ko",
"widgets_seed": "iri",
"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_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",
@ -334,21 +281,15 @@
"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_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_banned_for": "An haramta don",
"auth_store_banned_minutes": "da minti",
"auth_store_incorrect_password": "PIN na gaskiya",
"wallet_store_monero_wallet": "Monero walat",
"wallet_restoration_store_incorrect_seed_length": "kalmar sirrin iri ba daidai ba",
"full_balance": "DUKAN KUDI",
"available_balance": "KUDI",
"hidden_balance": "BOYE KUDI",
"sync_status_syncronizing": "KWAFI",
"sync_status_syncronized": "KYAU",
"sync_status_not_connected": "BABU INTERNET",
@ -357,21 +298,15 @@
"sync_status_connecting": "HADA",
"sync_status_connected": "HANNU",
"sync_status_attempting_sync": "KWAFI",
"transaction_priority_slow": "SAURI DA SAURI",
"transaction_priority_regular": "SAURI NORMAL",
"transaction_priority_medium": "SAURI DA DADI",
"transaction_priority_fast": "sauri",
"transaction_priority_fastest": "mafi sauri",
"trade_for_not_created": "Ba a ƙirƙira ciniki don ${title} ba.",
"trade_not_created": "Ba a ƙirƙira ciniki ba",
"trade_id_not_found": "Ba a samo cinikin ${tradeId} na ${title} ba.",
"trade_not_found": "Ba a sami ciniki ba.",
"trade_state_pending": "Jira",
"trade_state_confirming": "Tabbatar",
"trade_state_trading": "Ciniki",
@ -386,59 +321,42 @@
"trade_state_timeout": "lokacin da ya ƙare",
"trade_state_created": "an halicci",
"trade_state_finished": "an kammala",
"change_language": "canja harshen",
"change_language_to": "canja harshen zuwa ${language}?",
"paste": "Manna",
"restore_from_seed_placeholder": "Da fatan za a shigar da ko manna maɓallin ku a nan",
"add_new_word": "Ƙara kalma sabuwa",
"incorrect_seed": "rubutun da aka shigar ba shi da inganci.",
"biometric_auth_reason": "Duba hoton yatsa don tantancewa",
"version": "Sigar ${currentVersion}",
"openalias_alert_title": "An gano adireshin",
"openalias_alert_content": "Zaka aika kuɗi zuwa \n${recipient_name}",
"card_address": "Adireshin:",
"buy": "Sayi",
"sell": "sayar",
"placeholder_transactions": "Za a nuna ma'amalolin ku anan",
"placeholder_contacts": "Za a nuna lambobin sadarwar ku anan",
"template": "Samfura",
"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?",
"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_content": "Kana so ka canja walat yanzu zuwa ${wallet_name}?",
"creating_new_wallet": "Haliccin walat sabuwa",
"creating_new_wallet_error": "Kuskure: ${description}",
"seed_alert_title": "Hankali",
"seed_alert_content": "Irin ita ce kawai hanya don dawo da walat ɗin ku. Kun rubuta shi?",
"seed_alert_back": "juya baya",
"seed_alert_yes": "E, Na yi",
"exchange_sync_alert_content": "Da fatan za a jira har sai an daidaita walat ɗin ku",
"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_button_text": "Ina fahimta. Nuna mini seed din nawa",
"xmr_to_error": "XMR.TO kuskure",
"xmr_to_error_description": "Adadin ba shi da inganci. Maksimum ɗaura 8 digiri bayan decimal point",
"provider_error": "${provider} kuskure",
"use_ssl": "Yi amfani da SSL",
"trusted": "Amintacce",
"color_theme": "Jigon launi",
"light_theme": "Haske",
"bright_theme": "Mai haske",
@ -451,11 +369,9 @@
"transaction_key": "Aikace-aikacen key",
"confirmations": "Tabbatar",
"recipient_address": "Adireshin mai karɓa",
"extra_id": "Karin ID:",
"destination_tag": "Tambarin makoma:",
"memo": "Memo:",
"backup": "Ajiyayyen",
"change_password": "Canza kalmar shiga",
"backup_password": "Ajiyayyen kalmar sirri",
@ -463,55 +379,40 @@
"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.",
"backup_file": "Ajiyayyen fayil",
"edit_backup_password": "Shirya Kalmar wucewa ta Ajiyayyen",
"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?",
"enter_backup_password": "Shigar da kalmar wucewa ta madadin nan",
"select_backup_file": "Zaɓi fayil ɗin madadin",
"import": "Shigo da",
"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_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",
"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.",
"confirmed": "An tabbatar",
"unconfirmed": "Ba a tabbatar ba",
"displayable": "Ana iya nunawa",
"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.",
"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.",
"understand": "na gane",
"apk_update": "apk sabunta",
"buy_bitcoin": "Sayi Bitcoin",
"buy_with": "Saya da",
"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",
"do_not_show_me": "Kar ka sake nuna min wannan",
"unspent_coins_title": "Tsabar da ba a kashe ba",
"unspent_coins_details_title": "Bayanan tsabar kudi da ba a kashe ba",
"freeze": "Daskare",
"frozen": "Daskararre",
"coin_control": "Sarrafa tsabar kuɗi (na zaɓi)",
"address_detected": "An gano adireshin",
"address_from_domain": "Wannan adireshin ya fito daga ${domain} akan Unstoppable Domain",
"add_receiver": "Ƙara wani mai karɓa (na zaɓi)",
"manage_yats": "Sarrafa Yats",
"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.",
@ -681,11 +582,10 @@
"contact_list_contacts": "Lambobin sadarwa",
"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.",
"send_to_this_address" : "Aiko ${currency} ${tag} zuwa adireshin wannan",
"arrive_in_this_address" : "${currency} ${tag} zai je wurin wannan adireshi",
"send_to_this_address": "Aiko ${currency} ${tag} zuwa adireshin wannan",
"arrive_in_this_address": "${currency} ${tag} zai je wurin wannan adireshi",
"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.",
"scan_qr_code": "Gani QR kodin",
"cold_or_recover_wallet": "Samun kashi na baya ko samun kashi na kasa",
"please_wait": "Don Allah a rufe",
"sweeping_wallet": "Kashi na kasa",
@ -705,10 +605,18 @@
"frozen_balance": "Falin kuma maɓallin",
"settings": "Saiti",
"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_above_maximum_limit" : "Kudin da ya kamata",
"show_market_place" :"Nuna dan kasuwa",
"error_text_input_below_minimum_limit": "Kudin ba a kamai",
"error_text_input_above_maximum_limit": "Kudin da ya kamata",
"show_market_place": "Nuna dan kasuwa",
"prevent_screenshots": "Fada lambobi da jarrabobi na kayan lambobi",
"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