Merge branch 'main' of https://github.com/cake-tech/cake_wallet into CW-237-enhance-error-catching-reporting

This commit is contained in:
OmarHatem
2023-01-06 16:18:21 +02:00
166 changed files with 4306 additions and 2203 deletions
+4
View File
@@ -51,6 +51,10 @@ class CWBitcoin extends Bitcoin {
TransactionPriority deserializeBitcoinTransactionPriority(int raw)
=> BitcoinTransactionPriority.deserialize(raw: raw);
@override
TransactionPriority deserializeLitecoinTransactionPriority(int raw)
=> LitecoinTransactionPriority.deserialize(raw: raw);
@override
int getFeeRate(Object wallet, TransactionPriority priority) {
final bitcoinWallet = wallet as ElectrumWallet;
+1 -3
View File
@@ -24,7 +24,6 @@ class AddressValidator extends TextValidator {
return '[0-9a-zA-Z_]';
case CryptoCurrency.usdc:
case CryptoCurrency.usdcpoly:
case CryptoCurrency.husd:
case CryptoCurrency.ape:
case CryptoCurrency.avaxc:
case CryptoCurrency.eth:
@@ -156,7 +155,7 @@ class AddressValidator extends TextValidator {
return [98, 99, 106];
case CryptoCurrency.btt:
return [34];
case CryptoCurrency.bttbsc:
case CryptoCurrency.bttc:
return [34];
case CryptoCurrency.doge:
return [34];
@@ -181,7 +180,6 @@ class AddressValidator extends TextValidator {
case CryptoCurrency.stx:
return [40, 41, 42];
case CryptoCurrency.usdcpoly:
case CryptoCurrency.husd:
case CryptoCurrency.mana:
case CryptoCurrency.matic:
case CryptoCurrency.maticpoly:
+30 -3
View File
@@ -4,12 +4,19 @@ import 'package:shared_preferences/shared_preferences.dart';
import 'package:cake_wallet/entities/preferences_key.dart';
import 'package:cake_wallet/entities/secret_store_key.dart';
import 'package:cake_wallet/entities/encrypt.dart';
import 'package:cake_wallet/di.dart';
import 'package:cake_wallet/store/settings_store.dart';
class AuthService with Store {
AuthService({required this.secureStorage, required this.sharedPreferences});
AuthService({
required this.secureStorage,
required this.sharedPreferences,
required this.settingsStore,
});
final FlutterSecureStorage secureStorage;
final SharedPreferences sharedPreferences;
final SettingsStore settingsStore;
Future<void> setPassword(String password) async {
final key = generateStoreKeyFor(key: SecretStoreKey.pinCodePassword);
@@ -19,8 +26,7 @@ class AuthService with Store {
Future<bool> canAuthenticate() async {
final key = generateStoreKeyFor(key: SecretStoreKey.pinCodePassword);
final walletName =
sharedPreferences.getString(PreferencesKey.currentWalletName) ?? '';
final walletName = sharedPreferences.getString(PreferencesKey.currentWalletName) ?? '';
var password = '';
try {
@@ -39,4 +45,25 @@ class AuthService with Store {
return decodedPin == pin;
}
void saveLastAuthTime() {
int timestamp = DateTime.now().millisecondsSinceEpoch;
sharedPreferences.setInt(PreferencesKey.lastAuthTimeMilliseconds, timestamp);
}
bool requireAuth() {
final timestamp = sharedPreferences.getInt(PreferencesKey.lastAuthTimeMilliseconds);
final duration = _durationToRequireAuth(timestamp ?? 0);
final requiredPinInterval = settingsStore.pinTimeOutDuration;
return duration >= requiredPinInterval.value;
}
int _durationToRequireAuth(int timestamp) {
DateTime before = DateTime.fromMillisecondsSinceEpoch(timestamp);
DateTime now = DateTime.now();
Duration timeDifference = now.difference(before);
return timeDifference.inMinutes;
}
}
+14
View File
@@ -214,8 +214,10 @@ class BackupService {
final currentBitcoinElectrumSererId = data[PreferencesKey.currentBitcoinElectrumSererIdKey] as int?;
final currentLanguageCode = data[PreferencesKey.currentLanguageCode] as String?;
final displayActionListMode = data[PreferencesKey.displayActionListModeKey] as int?;
final fiatApiMode = data[PreferencesKey.currentFiatApiModeKey] as int?;
final currentPinLength = data[PreferencesKey.currentPinLength] as int?;
final currentTheme = data[PreferencesKey.currentTheme] as int?;
final disableExchange = data[PreferencesKey.disableExchangeKey] as bool?;
final currentDefaultSettingsMigrationVersion = data[PreferencesKey.currentDefaultSettingsMigrationVersion] as int?;
final moneroTransactionPriority = data[PreferencesKey.moneroTransactionPriority] as int?;
final bitcoinTransactionPriority = data[PreferencesKey.bitcoinTransactionPriority] as int?;
@@ -266,6 +268,10 @@ class BackupService {
await _sharedPreferences.setInt(PreferencesKey.displayActionListModeKey,
displayActionListMode);
if (fiatApiMode != null)
await _sharedPreferences.setInt(PreferencesKey.currentFiatApiModeKey,
fiatApiMode);
if (currentPinLength != null)
await _sharedPreferences.setInt(PreferencesKey.currentPinLength,
currentPinLength);
@@ -274,6 +280,10 @@ class BackupService {
await _sharedPreferences.setInt(
PreferencesKey.currentTheme, currentTheme);
if (disableExchange != null)
await _sharedPreferences.setBool(
PreferencesKey.disableExchangeKey, disableExchange);
if (currentDefaultSettingsMigrationVersion != null)
await _sharedPreferences.setInt(
PreferencesKey.currentDefaultSettingsMigrationVersion,
@@ -421,12 +431,16 @@ class BackupService {
_sharedPreferences.getInt(PreferencesKey.displayActionListModeKey),
PreferencesKey.currentTheme:
_sharedPreferences.getInt(PreferencesKey.currentTheme),
PreferencesKey.disableExchangeKey:
_sharedPreferences.getBool(PreferencesKey.disableExchangeKey),
PreferencesKey.currentDefaultSettingsMigrationVersion: _sharedPreferences
.getInt(PreferencesKey.currentDefaultSettingsMigrationVersion),
PreferencesKey.bitcoinTransactionPriority:
_sharedPreferences.getInt(PreferencesKey.bitcoinTransactionPriority),
PreferencesKey.moneroTransactionPriority:
_sharedPreferences.getInt(PreferencesKey.moneroTransactionPriority),
PreferencesKey.currentFiatApiModeKey:
_sharedPreferences.getInt(PreferencesKey.currentFiatApiModeKey),
};
return json.encode(preferences);
+2 -2
View File
@@ -14,8 +14,8 @@ String syncStatusTitle(SyncStatus syncStatus) {
return S.current.sync_status_not_connected;
}
if (syncStatus is StartingSyncStatus) {
return S.current.sync_status_starting_sync;
if (syncStatus is AttemptingSyncStatus) {
return S.current.sync_status_attempting_sync;
}
if (syncStatus is FailedSyncStatus) {
+72 -33
View File
@@ -5,9 +5,15 @@ import 'package:cake_wallet/ionia/ionia_anypay.dart';
import 'package:cake_wallet/ionia/ionia_gift_card.dart';
import 'package:cake_wallet/ionia/ionia_tip.dart';
import 'package:cake_wallet/src/screens/buy/onramper_page.dart';
import 'package:cake_wallet/src/screens/settings/display_settings_page.dart';
import 'package:cake_wallet/src/screens/settings/other_settings_page.dart';
import 'package:cake_wallet/src/screens/settings/privacy_page.dart';
import 'package:cake_wallet/src/screens/settings/security_backup_page.dart';
import 'package:cake_wallet/src/screens/ionia/cards/ionia_custom_redeem_page.dart';
import 'package:cake_wallet/src/screens/ionia/cards/ionia_gift_card_detail_page.dart';
import 'package:cake_wallet/src/screens/ionia/cards/ionia_more_options_page.dart';
import 'package:cake_wallet/src/screens/settings/connection_sync_page.dart';
import 'package:cake_wallet/utils/payment_request.dart';
import 'package:cake_wallet/view_model/ionia/ionia_auth_view_model.dart';
import 'package:cake_wallet/view_model/ionia/ionia_buy_card_view_model.dart';
import 'package:cake_wallet/view_model/ionia/ionia_custom_tip_view_model.dart';
@@ -26,6 +32,11 @@ import 'package:cake_wallet/src/screens/dashboard/widgets/balance_page.dart';
import 'package:cake_wallet/view_model/ionia/ionia_account_view_model.dart';
import 'package:cake_wallet/view_model/ionia/ionia_gift_cards_list_view_model.dart';
import 'package:cake_wallet/view_model/ionia/ionia_purchase_merch_view_model.dart';
import 'package:cake_wallet/view_model/settings/display_settings_view_model.dart';
import 'package:cake_wallet/view_model/settings/other_settings_view_model.dart';
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:cw_core/unspent_coins_info.dart';
import 'package:cake_wallet/core/backup_service.dart';
import 'package:cw_core/wallet_service.dart';
@@ -49,7 +60,6 @@ import 'package:cake_wallet/src/screens/exchange_trade/exchange_trade_page.dart'
import 'package:cake_wallet/src/screens/faq/faq_page.dart';
import 'package:cake_wallet/src/screens/new_wallet/new_wallet_type_page.dart';
import 'package:cake_wallet/src/screens/nodes/node_create_or_edit_page.dart';
import 'package:cake_wallet/src/screens/nodes/nodes_list_page.dart';
import 'package:cake_wallet/src/screens/order_details/order_details_page.dart';
import 'package:cake_wallet/src/screens/pin_code/pin_code_widget.dart';
import 'package:cake_wallet/src/screens/rescan/rescan_page.dart';
@@ -59,7 +69,6 @@ import 'package:cake_wallet/src/screens/restore/wallet_restore_page.dart';
import 'package:cake_wallet/src/screens/seed/pre_seed_page.dart';
import 'package:cake_wallet/src/screens/seed/wallet_seed_page.dart';
import 'package:cake_wallet/src/screens/send/send_template_page.dart';
import 'package:cake_wallet/src/screens/settings/settings.dart';
import 'package:cake_wallet/src/screens/setup_pin_code/setup_pin_code.dart';
import 'package:cake_wallet/src/screens/support/support_page.dart';
import 'package:cake_wallet/src/screens/trade_details/trade_details_page.dart';
@@ -115,7 +124,6 @@ import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_v
import 'package:cake_wallet/view_model/monero_account_list/monero_account_edit_or_create_view_model.dart';
import 'package:cake_wallet/view_model/monero_account_list/monero_account_list_view_model.dart';
import 'package:cake_wallet/view_model/send/send_view_model.dart';
import 'package:cake_wallet/view_model/settings/settings_view_model.dart';
import 'package:cake_wallet/view_model/wallet_keys_view_model.dart';
import 'package:cake_wallet/view_model/wallet_list/wallet_list_view_model.dart';
import 'package:cake_wallet/view_model/wallet_restore_view_model.dart';
@@ -153,6 +161,7 @@ import 'package:cake_wallet/anypay/any_pay_payment_committed_info.dart';
import 'package:cake_wallet/ionia/ionia_any_pay_payment_info.dart';
import 'package:cake_wallet/src/screens/receive/fullscreen_qr_page.dart';
import 'package:cake_wallet/core/wallet_loading_service.dart';
import 'package:cw_core/crypto_currency.dart';
final getIt = GetIt.instance;
@@ -300,7 +309,10 @@ Future setup(
getIt.registerFactory<AuthService>(() => AuthService(
secureStorage: getIt.get<FlutterSecureStorage>(),
sharedPreferences: getIt.get<SharedPreferences>()));
sharedPreferences: getIt.get<SharedPreferences>(),
settingsStore: getIt.get<SettingsStore>(),
),
);
getIt.registerFactory<AuthViewModel>(() => AuthViewModel(
getIt.get<AuthService>(),
@@ -343,7 +355,7 @@ Future setup(
onAuthenticationFinished: onAuthFinished,
closable: closable ?? false));
getIt.registerFactory(() =>
getIt.registerFactory(() =>
BalancePage(dashboardViewModel: getIt.get<DashboardViewModel>(), settingsStore: getIt.get<SettingsStore>()));
getIt.registerFactory<DashboardPage>(() => DashboardPage( balancePage: getIt.get<BalancePage>(), walletViewModel: getIt.get<DashboardViewModel>(), addressListViewModel: getIt.get<WalletAddressListViewModel>()));
@@ -376,9 +388,11 @@ Future setup(
getIt.get<BalanceViewModel>(),
_transactionDescriptionBox));
getIt.registerFactory(
() => SendPage(sendViewModel: getIt.get<SendViewModel>(),
settingsViewModel: getIt.get<SettingsViewModel>()));
getIt.registerFactoryParam<SendPage, PaymentRequest?, void>(
(PaymentRequest? initialPaymentRequest, _) => SendPage(
sendViewModel: getIt.get<SendViewModel>(),
initialPaymentRequest: initialPaymentRequest,
));
getIt.registerFactory(() => SendTemplatePage(
sendTemplateViewModel: getIt.get<SendTemplateViewModel>()));
@@ -386,7 +400,10 @@ Future setup(
getIt.registerFactory(() => WalletListViewModel(
_walletInfoSource,
getIt.get<AppStore>(),
getIt.get<WalletLoadingService>()));
getIt.get<WalletLoadingService>(),
getIt.get<AuthService>(),
),
);
getIt.registerFactory(() =>
WalletListPage(walletListViewModel: getIt.get<WalletListViewModel>()));
@@ -434,12 +451,20 @@ Future setup(
getIt.get<MoneroAccountEditOrCreateViewModel>(param1: account)));
getIt.registerFactory(() {
final appStore = getIt.get<AppStore>();
final yatStore = getIt.get<YatStore>();
return SettingsViewModel(appStore.settingsStore, yatStore, appStore.wallet!);
return DisplaySettingsViewModel(getIt.get<SettingsStore>());
});
getIt.registerFactory(() => SettingsPage(getIt.get<SettingsViewModel>()));
getIt.registerFactory(() {
return PrivacySettingsViewModel(getIt.get<SettingsStore>());
});
getIt.registerFactory(() {
return OtherSettingsViewModel(getIt.get<SettingsStore>(), getIt.get<AppStore>().wallet!);
});
getIt.registerFactory(() {
return SecuritySettingsViewModel(getIt.get<SettingsStore>(), getIt.get<AuthService>());
});
getIt
.registerFactory(() => WalletSeedViewModel(getIt.get<AppStore>().wallet!));
@@ -458,12 +483,11 @@ Future setup(
(ContactRecord? contact, _) =>
ContactViewModel(_contactSource, contact: contact));
getIt.registerFactory(
() => ContactListViewModel(_contactSource, _walletInfoSource));
getIt.registerFactoryParam<ContactListViewModel, CryptoCurrency?, void>(
(CryptoCurrency? cur, _) => ContactListViewModel(_contactSource, _walletInfoSource, cur));
getIt.registerFactoryParam<ContactListPage, bool, void>(
(bool isEditable, _) => ContactListPage(getIt.get<ContactListViewModel>(),
isEditable: isEditable));
getIt.registerFactoryParam<ContactListPage, CryptoCurrency?, void>((CryptoCurrency? cur, _)
=> ContactListPage(getIt.get<ContactListViewModel>(param1: cur)));
getIt.registerFactoryParam<ContactPage, ContactRecord?, void>(
(ContactRecord? contact, _) =>
@@ -475,10 +499,22 @@ Future setup(
_nodeSource, appStore.wallet!, appStore.settingsStore);
});
getIt.registerFactory(() => NodeListPage(getIt.get<NodeListViewModel>()));
getIt.registerFactory(() => ConnectionSyncPage(getIt.get<NodeListViewModel>(), getIt.get<DashboardViewModel>()));
getIt.registerFactory(() =>
NodeCreateOrEditViewModel(_nodeSource, getIt.get<AppStore>().wallet!));
getIt.registerFactory(() => SecurityBackupPage(getIt.get<SecuritySettingsViewModel>()));
getIt.registerFactory(() => PrivacyPage(getIt.get<PrivacySettingsViewModel>()));
getIt.registerFactory(() => DisplaySettingsPage(getIt.get<DisplaySettingsViewModel>()));
getIt.registerFactory(() => OtherSettingsPage(getIt.get<OtherSettingsViewModel>()));
getIt.registerFactoryParam<NodeCreateOrEditViewModel, WalletType?, void>(
(WalletType? type, _) => NodeCreateOrEditViewModel(
_nodeSource,
type ?? getIt.get<AppStore>().wallet!.type,
getIt.get<SettingsStore>(),
));
getIt.registerFactory(
() => NodeCreateOrEditPage(getIt.get<NodeCreateOrEditViewModel>()));
@@ -494,7 +530,6 @@ Future setup(
getIt.get<TradesStore>(),
getIt.get<AppStore>().settingsStore,
getIt.get<SharedPreferences>(),
getIt.get<SettingsViewModel>(),
));
getIt.registerFactory(() => ExchangeTradeViewModel(
@@ -678,7 +713,7 @@ Future setup(
getIt.registerFactoryParam<FullscreenQRPage, String, bool>(
(String qrData, bool isLight) => FullscreenQRPage(qrData: qrData, isLight: isLight,));
getIt.registerFactory(() => IoniaApi());
getIt.registerFactory(() => AnyPayApi());
@@ -698,7 +733,7 @@ Future setup(
getIt.registerFactoryParam<IoniaMerchPurchaseViewModel, double, IoniaMerchant>((double amount, merchant) {
return IoniaMerchPurchaseViewModel(
ioniaAnyPayService: getIt.get<IoniaAnyPay>(),
ioniaAnyPayService: getIt.get<IoniaAnyPay>(),
amount: amount,
ioniaMerchant: merchant,
sendViewModel: getIt.get<SendViewModel>()
@@ -741,31 +776,32 @@ Future setup(
ioniaService: getIt.get<IoniaService>(),
giftCard: giftCard);
});
getIt.registerFactoryParam<IoniaCustomTipViewModel, List, void>((List args, _) {
final amount = args[0] as double;
final merchant = args[1] as IoniaMerchant;
final tip = args[2] as IoniaTip;
return IoniaCustomTipViewModel(amount: amount, tip: tip, ioniaMerchant: merchant);
});
getIt.registerFactoryParam<IoniaGiftCardDetailPage, IoniaGiftCard, void>((IoniaGiftCard giftCard, _) {
return IoniaGiftCardDetailPage(getIt.get<IoniaGiftCardDetailsViewModel>(param1: giftCard));
});
getIt.registerFactoryParam<IoniaMoreOptionsPage, List, void>((List args, _){
final giftCard = args.first as IoniaGiftCard;
return IoniaMoreOptionsPage(giftCard);
return IoniaMoreOptionsPage(giftCard);
});
getIt.registerFactoryParam<IoniaCustomRedeemViewModel, IoniaGiftCard, void>((IoniaGiftCard giftCard, _) => IoniaCustomRedeemViewModel(giftCard));
getIt.registerFactoryParam<IoniaCustomRedeemViewModel, IoniaGiftCard, void>((IoniaGiftCard giftCard, _)
=> IoniaCustomRedeemViewModel(giftCard: giftCard, ioniaService: getIt.get<IoniaService>()));
getIt.registerFactoryParam<IoniaCustomRedeemPage, List, void>((List args, _){
final giftCard = args.first as IoniaGiftCard;
return IoniaCustomRedeemPage(getIt.get<IoniaCustomRedeemViewModel>(param1: giftCard) );
return IoniaCustomRedeemPage(getIt.get<IoniaCustomRedeemViewModel>(param1: giftCard) );
});
@@ -794,5 +830,8 @@ Future setup(
(IoniaAnyPayPaymentInfo paymentInfo, AnyPayPaymentCommittedInfo committedInfo)
=> IoniaPaymentStatusPage(getIt.get<IoniaPaymentStatusViewModel>(param1: paymentInfo, param2: committedInfo)));
getIt.registerFactoryParam<AdvancedPrivacySettingsViewModel, WalletType, void>((type, _) =>
AdvancedPrivacySettingsViewModel(type, getIt.get<SettingsStore>()));
_isSetupFinished = true;
}
}
+53 -10
View File
@@ -69,6 +69,8 @@ Future defaultSettingsMigration(
sharedPreferences: sharedPreferences, nodes: nodes);
await changeLitecoinCurrentElectrumServerToDefault(
sharedPreferences: sharedPreferences, nodes: nodes);
await changeHavenCurrentNodeToDefault(
sharedPreferences: sharedPreferences, nodes: nodes);
break;
case 2:
@@ -133,19 +135,48 @@ Future defaultSettingsMigration(
await changeDefaultHavenNode(nodes);
break;
case 18:
await addOnionNode(nodes);
break;
case 19:
await validateBitcoinSavedTransactionPriority(sharedPreferences);
break;
default:
break;
}
await sharedPreferences.setInt(
'current_default_settings_migration_version', version);
PreferencesKey.currentDefaultSettingsMigrationVersion, version);
} catch (e) {
print('Migration error: ${e.toString()}');
}
});
await sharedPreferences.setInt(
'current_default_settings_migration_version', version);
PreferencesKey.currentDefaultSettingsMigrationVersion, version);
}
Future<void> validateBitcoinSavedTransactionPriority(SharedPreferences sharedPreferences) async {
if (bitcoin == null) {
return;
}
final int? savedBitcoinPriority =
sharedPreferences.getInt(PreferencesKey.bitcoinTransactionPriority);
if (!bitcoin!.getTransactionPriorities().any((element) => element.raw == savedBitcoinPriority)) {
await sharedPreferences.setInt(
PreferencesKey.bitcoinTransactionPriority, bitcoin!.getMediumTransactionPriority().serialize());
}
}
Future<void> addOnionNode(Box<Node> nodes) async {
final onionNodeUri = "cakexmrl7bonq7ovjka5kuwuyd3f7qnkz6z6s6dmsy3uckwra7bvggyd.onion:18081";
// check if the user has this node before (added it manually)
if (nodes.values.firstWhereOrNull((element) => element.uriRaw == onionNodeUri) == null) {
await nodes.add(Node(uri: onionNodeUri, type: WalletType.monero));
}
}
Future<void> replaceNodesMigration({required Box<Node> nodes}) async {
@@ -176,7 +207,7 @@ Future<void> changeMoneroCurrentNodeToDefault(
final node = getMoneroDefaultNode(nodes: nodes);
final nodeId = node?.key as int ?? 0; // 0 - England
await sharedPreferences.setInt('current_node_id', nodeId);
await sharedPreferences.setInt(PreferencesKey.currentNodeIdKey, nodeId);
}
Node? getBitcoinDefaultElectrumServer({required Box<Node> nodes}) {
@@ -223,7 +254,7 @@ Future<void> changeBitcoinCurrentElectrumServerToDefault(
final server = getBitcoinDefaultElectrumServer(nodes: nodes);
final serverId = server?.key as int ?? 0;
await sharedPreferences.setInt('current_node_id_btc', serverId);
await sharedPreferences.setInt(PreferencesKey.currentBitcoinElectrumSererIdKey, serverId);
}
Future<void> changeLitecoinCurrentElectrumServerToDefault(
@@ -232,7 +263,7 @@ Future<void> changeLitecoinCurrentElectrumServerToDefault(
final server = getLitecoinDefaultElectrumServer(nodes: nodes);
final serverId = server?.key as int ?? 0;
await sharedPreferences.setInt('current_node_id_ltc', serverId);
await sharedPreferences.setInt(PreferencesKey.currentLitecoinElectrumSererIdKey, serverId);
}
Future<void> changeHavenCurrentNodeToDefault(
@@ -252,7 +283,7 @@ Future<void> replaceDefaultNode(
'eu-node.cakewallet.io:18081',
'node.cakewallet.io:18081'
];
final currentNodeId = sharedPreferences.getInt('current_node_id');
final currentNodeId = sharedPreferences.getInt(PreferencesKey.currentNodeIdKey);
final currentNode =
nodes.values.firstWhereOrNull((Node node) => node.key == currentNodeId);
final needToReplace =
@@ -277,17 +308,29 @@ Future<void> updateNodeTypes({required Box<Node> nodes}) async {
Future<void> addBitcoinElectrumServerList({required Box<Node> nodes}) async {
final serverList = await loadBitcoinElectrumServerList();
await nodes.addAll(serverList);
for (var node in serverList) {
if (nodes.values.firstWhereOrNull((element) => element.uriRaw == node.uriRaw) == null) {
await nodes.add(node);
}
}
}
Future<void> addLitecoinElectrumServerList({required Box<Node> nodes}) async {
final serverList = await loadLitecoinElectrumServerList();
await nodes.addAll(serverList);
for (var node in serverList) {
if (nodes.values.firstWhereOrNull((element) => element.uriRaw == node.uriRaw) == null) {
await nodes.add(node);
}
}
}
Future<void> addHavenNodeList({required Box<Node> nodes}) async {
final nodeList = await loadDefaultHavenNodes();
await nodes.addAll(nodeList);
for (var node in nodeList) {
if (nodes.values.firstWhereOrNull((element) => element.uriRaw == node.uriRaw) == null) {
await nodes.add(node);
}
}
}
Future<void> addAddressesForMoneroWallets(
@@ -431,7 +474,7 @@ Future<void> resetBitcoinElectrumServer(
final oldElectrumServer = nodeSource.values.firstWhereOrNull(
(node) => node.uri.toString().contains('electrumx.cakewallet.com'));
var cakeWalletNode = nodeSource.values.firstWhereOrNull(
(node) => node.uri.toString() == cakeWalletBitcoinElectrumUri);
(node) => node.uriRaw.toString() == cakeWalletBitcoinElectrumUri);
if (cakeWalletNode == null) {
cakeWalletNode =
+39
View File
@@ -0,0 +1,39 @@
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cw_core/enumerable_item.dart';
class FiatApiMode extends EnumerableItem<int> with Serializable<int> {
const FiatApiMode({required String title, required int raw}) : super(title: title, raw: raw);
static const all = [FiatApiMode.enabled, FiatApiMode.torOnly, FiatApiMode.disabled];
static const enabled = FiatApiMode(raw: 0, title: 'Enabled');
static const torOnly = FiatApiMode(raw: 1, title: 'Tor only');
static const disabled = FiatApiMode(raw: 2, title: 'Disabled');
static FiatApiMode deserialize({required int raw}) {
switch (raw) {
case 0:
return enabled;
case 1:
return torOnly;
case 2:
return disabled;
default:
throw Exception('Unexpected token: $raw for FiatApiMode deserialize');
}
}
@override
String toString() {
switch (this) {
case FiatApiMode.enabled:
return S.current.enabled;
case FiatApiMode.torOnly:
return S.current.tor_only;
case FiatApiMode.disabled:
return S.current.disabled;
default:
return '';
}
}
}
+4 -2
View File
@@ -18,7 +18,8 @@ class LanguageService {
'uk': 'Українська (Ukrainian)',
'zh': '中文 (Chinese)',
'hr': 'Hrvatski (Croatian)',
'it': 'Italiano (Italian)'
'it': 'Italiano (Italian)',
'th': 'ภาษาไทย (Thai)'
};
static const Map<String, String> localeCountryCode = {
@@ -36,7 +37,8 @@ class LanguageService {
'uk': 'ukr',
'zh': 'chn',
'hr': 'hrv',
'it': 'ita'
'it': 'ita',
'th': 'tha'
};
static final list = <String, String> {};
+1 -2
View File
@@ -4,7 +4,6 @@ import 'package:cake_wallet/entities/parsed_address.dart';
import 'package:cake_wallet/entities/unstoppable_domain_address.dart';
import 'package:cake_wallet/entities/emoji_string_extension.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:flutter/foundation.dart';
import 'package:cake_wallet/entities/fio_address_provider.dart';
class AddressResolver {
@@ -51,7 +50,7 @@ class AddressResolver {
return ParsedAddress(addresses: [text]);
}
if (unstoppableDomains.any((domain) => name.contains(domain))) {
if (unstoppableDomains.any((domain) => name.trim() == domain)) {
final address = await fetchUnstoppableDomainAddress(text, ticker);
return ParsedAddress.fetchUnstoppableDomainAddress(address: address, name: text);
}
@@ -0,0 +1,32 @@
import 'package:cake_wallet/generated/i18n.dart';
enum PinCodeRequiredDuration {
always(0),
tenminutes(10),
onehour(60);
const PinCodeRequiredDuration(this.value);
final int value;
static PinCodeRequiredDuration deserialize({required int raw}) =>
PinCodeRequiredDuration.values.firstWhere((e) => e.value == raw);
@override
String toString(){
String label = '';
switch (this) {
case PinCodeRequiredDuration.always:
label = S.current.always;
break;
case PinCodeRequiredDuration.tenminutes:
label = S.current.minutes_to_pin_code('10');
break;
case PinCodeRequiredDuration.onehour:
label = S.current.minutes_to_pin_code('60');
break;
}
return label;
}
}
+6
View File
@@ -9,6 +9,7 @@ class PreferencesKey {
static const currentTransactionPriorityKeyLegacy = 'current_fee_priority';
static const currentBalanceDisplayModeKey = 'current_balance_display_mode';
static const shouldSaveRecipientAddressKey = 'save_recipient_address';
static const currentFiatApiModeKey = 'current_fiat_api_mode';
static const allowBiometricalAuthenticationKey =
'allow_biometrical_authentication';
static const disableExchangeKey = 'disable_exchange';
@@ -21,9 +22,14 @@ class PreferencesKey {
'current_default_settings_migration_version';
static const moneroTransactionPriority = 'current_fee_priority_monero';
static const bitcoinTransactionPriority = 'current_fee_priority_bitcoin';
static const havenTransactionPriority = 'current_fee_priority_haven';
static const litecoinTransactionPriority = 'current_fee_priority_litecoin';
static const shouldShowReceiveWarning = 'should_show_receive_warning';
static const shouldShowYatPopup = 'should_show_yat_popup';
static const moneroWalletPasswordUpdateV1Base = 'monero_wallet_update_v1';
static const pinTimeOutDuration = 'pin_timeout_duration';
static const lastAuthTimeMilliseconds = 'last_auth_time_milliseconds';
static String moneroWalletUpdateV1Key(String name)
=> '${PreferencesKey.moneroWalletPasswordUpdateV1Base}_${name}';
@@ -0,0 +1,21 @@
import 'package:cake_wallet/bitcoin/bitcoin.dart';
import 'package:cake_wallet/haven/haven.dart';
import 'package:cake_wallet/monero/monero.dart';
import 'package:cw_core/transaction_priority.dart';
import 'package:cw_core/wallet_type.dart';
List<TransactionPriority> priorityForWalletType(WalletType type) {
switch (type) {
case WalletType.monero:
return monero!.getTransactionPriorities();
case WalletType.bitcoin:
return bitcoin!.getTransactionPriorities();
case WalletType.litecoin:
return bitcoin!.getLitecoinTransactionPriorities();
case WalletType.haven:
return haven!.getTransactionPriorities();
default:
return [];
}
}
@@ -11,7 +11,6 @@ import 'package:cake_wallet/exchange/trade_request.dart';
import 'package:cake_wallet/exchange/trade_state.dart';
import 'package:cake_wallet/exchange/changenow/changenow_request.dart';
import 'package:cake_wallet/exchange/exchange_provider_description.dart';
import 'package:cake_wallet/exchange/trade_not_created_exeption.dart';
class ChangeNowExchangeProvider extends ExchangeProvider {
ChangeNowExchangeProvider()
@@ -21,8 +20,7 @@ class ChangeNowExchangeProvider extends ExchangeProvider {
.where((i) => i != CryptoCurrency.xhv)
.map((i) => CryptoCurrency.all
.where((i) => i != CryptoCurrency.xhv)
.map((k) => ExchangePair(from: i, to: k, reverse: true))
.where((c) => c != null))
.map((k) => ExchangePair(from: i, to: k, reverse: true)))
.expand((i) => i)
.toList());
@@ -43,6 +41,9 @@ class ChangeNowExchangeProvider extends ExchangeProvider {
@override
bool get isEnabled => true;
@override
bool get supportsFixedRate => true;
@override
ExchangeProviderDescription get description =>
ExchangeProviderDescription.changeNow;
@@ -96,25 +97,36 @@ class ChangeNowExchangeProvider extends ExchangeProvider {
apiHeaderKey: apiKey,
'Content-Type': 'application/json'};
final flow = getFlow(isFixedRateMode);
final type = isFixedRateMode ? 'reverse' : 'direct';
final body = <String, String>{
'fromCurrency': normalizeCryptoCurrency(_request.from),
'toCurrency': normalizeCryptoCurrency(_request.to),
'fromNetwork': networkFor(_request.from),
'toNetwork': networkFor(_request.to),
'fromAmount': _request.fromAmount,
'toAmount': _request.toAmount,
if (!isFixedRateMode) 'fromAmount': _request.fromAmount,
if (isFixedRateMode) 'toAmount': _request.toAmount,
'address': _request.address,
'flow': flow,
'type': type,
'refundAddress': _request.refundAddress
};
if (isFixedRateMode) {
// since we schedule to calculate the rate every 5 seconds we need to ensure that
// we have the latest rate id with the given inputs before creating the trade
await fetchRate(
from: _request.from,
to: _request.to,
amount: double.tryParse(_request.toAmount) ?? 0,
isFixedRateMode: true,
isReceiveAmount: true,
);
body['rateId'] = _lastUsedRateId;
}
final uri = Uri.https(apiAuthority, createTradePath);
final response = await post(uri, headers: headers, body: json.encode(body));
if (response.statusCode == 400) {
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
final error = responseJSON['error'] as String;
@@ -130,7 +142,7 @@ class ChangeNowExchangeProvider extends ExchangeProvider {
final id = responseJSON['id'] as String;
final inputAddress = responseJSON['payinAddress'] as String;
final refundAddress = responseJSON['refundAddress'] as String;
final extraId = responseJSON['payinExtraId'] as String;
final extraId = responseJSON['payinExtraId'] as String?;
return Trade(
id: id,
@@ -141,7 +153,7 @@ class ChangeNowExchangeProvider extends ExchangeProvider {
refundAddress: refundAddress,
extraId: extraId,
createdAt: DateTime.now(),
amount: _request.fromAmount,
amount: responseJSON['fromAmount']?.toString() ?? _request.fromAmount,
state: TradeState.created);
}
@@ -180,9 +192,7 @@ class ChangeNowExchangeProvider extends ExchangeProvider {
final extraId = responseJSON['payinExtraId'] as String;
final outputTransaction = responseJSON['payoutHash'] as String;
final expiredAtRaw = responseJSON['validUntil'] as String;
final expiredAt = expiredAtRaw != null
? DateTime.parse(expiredAtRaw).toLocal()
: null;
final expiredAt = DateTime.tryParse(expiredAtRaw)?.toLocal();
return Trade(
id: id,
@@ -198,7 +208,7 @@ class ChangeNowExchangeProvider extends ExchangeProvider {
}
@override
Future<double> calculateAmount(
Future<double> fetchRate(
{required CryptoCurrency from,
required CryptoCurrency to,
required double amount,
@@ -214,10 +224,10 @@ class ChangeNowExchangeProvider extends ExchangeProvider {
final type = isReverse ? 'reverse' : 'direct';
final flow = getFlow(isFixedRateMode);
final params = <String, String>{
'fromCurrency': isReverse ? normalizeCryptoCurrency(to) : normalizeCryptoCurrency(from),
'toCurrency': isReverse ? normalizeCryptoCurrency(from) : normalizeCryptoCurrency(to),
'fromNetwork': isReverse ? networkFor(to) : networkFor(from),
'toNetwork': isReverse ? networkFor(from) : networkFor(to),
'fromCurrency': normalizeCryptoCurrency(from),
'toCurrency': normalizeCryptoCurrency(to),
'fromNetwork': networkFor(from),
'toNetwork': networkFor(to),
'type': type,
'flow': flow};
@@ -238,7 +248,7 @@ class ChangeNowExchangeProvider extends ExchangeProvider {
_lastUsedRateId = rateId;
}
return isReverse ? fromAmount : toAmount;
return isReverse ? (amount / fromAmount) : (toAmount / amount);
} catch(e) {
print(e.toString());
return 0.0;
+2 -2
View File
@@ -1,4 +1,3 @@
import 'package:flutter/foundation.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:cake_wallet/exchange/trade_request.dart';
import 'package:cake_wallet/exchange/exchange_pair.dart';
@@ -14,6 +13,7 @@ abstract class ExchangeProvider {
ExchangeProviderDescription get description;
bool get isAvailable;
bool get isEnabled;
bool get supportsFixedRate;
@override
String toString() => title;
@@ -26,7 +26,7 @@ abstract class ExchangeProvider {
required TradeRequest request,
required bool isFixedRateMode});
Future<Trade> findTradeById({required String id});
Future<double> calculateAmount({
Future<double> fetchRate({
required CryptoCurrency from,
required CryptoCurrency to,
required double amount,
@@ -24,6 +24,9 @@ class ExchangeProviderDescription extends EnumerableItem<int>
static const simpleSwap =
ExchangeProviderDescription(title: 'SimpleSwap', raw: 4, image: 'assets/images/simpleSwap.png');
static const all =
ExchangeProviderDescription(title: 'All trades', raw: 5, image:'');
static ExchangeProviderDescription deserialize({required int raw}) {
switch (raw) {
case 0:
@@ -36,6 +39,8 @@ class ExchangeProviderDescription extends EnumerableItem<int>
return sideShift;
case 4:
return simpleSwap;
case 5:
return all;
default:
throw Exception('Unexpected token: $raw for ExchangeProviderDescription deserialize');
}
@@ -66,6 +66,9 @@ class MorphTokenExchangeProvider extends ExchangeProvider {
@override
bool get isEnabled => true;
@override
bool get supportsFixedRate => false;
@override
ExchangeProviderDescription get description =>
ExchangeProviderDescription.morphToken;
@@ -200,7 +203,7 @@ class MorphTokenExchangeProvider extends ExchangeProvider {
}
@override
Future<double> calculateAmount(
Future<double> fetchRate(
{required CryptoCurrency from,
required CryptoCurrency to,
required double amount,
@@ -12,7 +12,6 @@ import 'package:cw_core/crypto_currency.dart';
import 'package:cake_wallet/exchange/trade_request.dart';
import 'package:cake_wallet/exchange/trade.dart';
import 'package:cake_wallet/exchange/limits.dart';
import 'package:flutter/foundation.dart';
import 'package:http/http.dart';
class SideShiftExchangeProvider extends ExchangeProvider {
@@ -28,7 +27,6 @@ class SideShiftExchangeProvider extends ExchangeProvider {
static const List<CryptoCurrency> _notSupported = [
CryptoCurrency.xhv,
CryptoCurrency.dcr,
CryptoCurrency.husd,
CryptoCurrency.kmd,
CryptoCurrency.mkr,
CryptoCurrency.near,
@@ -39,6 +37,7 @@ class SideShiftExchangeProvider extends ExchangeProvider {
CryptoCurrency.rvn,
CryptoCurrency.scrt,
CryptoCurrency.stx,
CryptoCurrency.bttc,
];
static List<ExchangePair> _supportedPairs() {
@@ -48,8 +47,7 @@ class SideShiftExchangeProvider extends ExchangeProvider {
return supportedCurrencies
.map((i) => supportedCurrencies
.map((k) => ExchangePair(from: i, to: k, reverse: true))
.where((c) => c != null))
.map((k) => ExchangePair(from: i, to: k, reverse: true)))
.expand((i) => i)
.toList();
}
@@ -59,7 +57,7 @@ class SideShiftExchangeProvider extends ExchangeProvider {
ExchangeProviderDescription.sideShift;
@override
Future<double> calculateAmount(
Future<double> fetchRate(
{required CryptoCurrency from,
required CryptoCurrency to,
required double amount,
@@ -81,9 +79,7 @@ class SideShiftExchangeProvider extends ExchangeProvider {
if (amount > max) return 0.00;
final estimatedAmount = rate * amount;
return estimatedAmount;
return rate;
} catch (_) {
return 0.00;
}
@@ -249,15 +245,15 @@ class SideShiftExchangeProvider extends ExchangeProvider {
final expectedSendAmount = responseJSON['depositAmount'].toString();
final deposits = responseJSON['deposits'] as List?;
TradeState? state;
String? status;
if (deposits != null && deposits.isNotEmpty) {
final status = deposits[0]['status'] as String;
state = TradeState.deserialize(raw: status);
if (deposits?.isNotEmpty ?? false) {
status = deposits![0]['status'] as String?;
}
state = TradeState.deserialize(raw: status ?? 'created');
final expiredAtRaw = responseJSON['expiresAtISO'] as String;
final expiredAt =
expiredAtRaw != null ? DateTime.parse(expiredAtRaw).toLocal() : null;
final expiredAt = DateTime.tryParse(expiredAtRaw)?.toLocal();
return Trade(
id: id,
@@ -277,6 +273,9 @@ class SideShiftExchangeProvider extends ExchangeProvider {
@override
bool get isEnabled => true;
@override
bool get supportsFixedRate => true;
@override
String get title => 'SideShift';
@@ -20,8 +20,7 @@ class SimpleSwapExchangeProvider extends ExchangeProvider {
.where((i) => i != CryptoCurrency.zaddr)
.map((i) => CryptoCurrency.all
.where((i) => i != CryptoCurrency.zaddr)
.map((k) => ExchangePair(from: i, to: k, reverse: true))
.where((c) => c != null))
.map((k) => ExchangePair(from: i, to: k, reverse: true)))
.expand((i) => i)
.toList());
@@ -37,7 +36,7 @@ class SimpleSwapExchangeProvider extends ExchangeProvider {
ExchangeProviderDescription.simpleSwap;
@override
Future<double> calculateAmount(
Future<double> fetchRate(
{required CryptoCurrency from,
required CryptoCurrency to,
required double amount,
@@ -59,9 +58,9 @@ class SimpleSwapExchangeProvider extends ExchangeProvider {
final uri = Uri.https(apiAuthority, getEstimatePath, params);
final response = await get(uri);
if (response.body == null || response.body == "null") return 0.00;
if (response.body == "null") return 0.00;
final data = json.decode(response.body) as String;
return double.parse(data);
return double.parse(data) / amount;
} catch (_) {
return 0.00;
}
@@ -210,6 +209,9 @@ class SimpleSwapExchangeProvider extends ExchangeProvider {
@override
bool get isEnabled => true;
@override
bool get supportsFixedRate => false;
@override
String get title => 'SimpleSwap';
@@ -48,6 +48,9 @@ class XMRTOExchangeProvider extends ExchangeProvider {
@override
bool get isEnabled => true;
@override
bool get supportsFixedRate => false;
@override
ExchangeProviderDescription get description =>
ExchangeProviderDescription.xmrto;
@@ -191,7 +194,7 @@ class XMRTOExchangeProvider extends ExchangeProvider {
}
@override
Future<double> calculateAmount(
Future<double> fetchRate(
{required CryptoCurrency from,
required CryptoCurrency to,
required double amount,
+1 -1
View File
@@ -37,7 +37,7 @@ class IoniaGiftCard {
purchaseAmount: element['PurchaseAmount'] as double,
actualAmount: element['ActualAmount'] as double,
totalTransactionAmount: element['TotalTransactionAmount'] as double,
totalDashTransactionAmount: element['TotalDashTransactionAmount'] as double,
totalDashTransactionAmount: (element['TotalDashTransactionAmount'] as double?) ?? 0.0,
remainingAmount: element['RemainingAmount'] as double,
isActive: element['IsActive'] as bool,
isEmpty: element['IsEmpty'] as bool,
+2 -2
View File
@@ -148,8 +148,8 @@ class IoniaService {
// Redeem
Future<void> redeem(IoniaGiftCard giftCard) async {
await chargeGiftCard(giftCardId: giftCard.id, amount: giftCard.remainingAmount);
Future<void> redeem({required int giftCardId, required double amount}) async {
await chargeGiftCard(giftCardId: giftCardId, amount: amount);
}
// Get Gift Card
+7 -8
View File
@@ -3,6 +3,7 @@ import 'dart:convert';
import 'dart:io';
import 'dart:isolate';
import 'package:cake_wallet/bitcoin/bitcoin.dart';
import 'package:cake_wallet/core/auth_service.dart';
import 'package:cake_wallet/entities/language_service.dart';
import 'package:cake_wallet/buy/order.dart';
import 'package:cake_wallet/entities/preferences_key.dart';
@@ -48,6 +49,7 @@ import 'package:cake_wallet/wallet_type_utils.dart';
final navigatorKey = GlobalKey<NavigatorState>();
final rootKey = GlobalKey<RootState>();
final RouteObserver<PageRoute> routeObserver = RouteObserver<PageRoute>();
Future<void> main() async {
@@ -166,7 +168,7 @@ Future<void> main() async {
exchangeTemplates: exchangeTemplates,
transactionDescriptions: transactionDescriptions,
secureStorage: secureStorage,
initialMigrationVersion: 17);
initialMigrationVersion: 19);
runApp(App());
}, (error, stackTrace) async {
print("@@@@@@@@@@@@@@@@ in run zone guard");
@@ -276,12 +278,6 @@ class AppState extends State<App> with SingleTickerProviderStateMixin {
//_handleInitialUri();
}
@override
void dispose() {
stream?.cancel();
super.dispose();
}
Future<void> _handleInitialUri() async {
try {
final uri = await getInitialUri();
@@ -329,11 +325,12 @@ class AppState extends State<App> with SingleTickerProviderStateMixin {
Widget build(BuildContext context) {
return Observer(builder: (BuildContext context) {
final appStore = getIt.get<AppStore>();
final authService = getIt.get<AuthService>();
final settingsStore = appStore.settingsStore;
final statusBarColor = Colors.transparent;
final authenticationStore = getIt.get<AuthenticationStore>();
final initialRoute =
authenticationStore.state == AuthenticationState.denied
authenticationStore.state == AuthenticationState.uninitialized
? Routes.disclaimer
: Routes.login;
final currentTheme = settingsStore.currentTheme;
@@ -353,7 +350,9 @@ class AppState extends State<App> with SingleTickerProviderStateMixin {
appStore: appStore,
authenticationStore: authenticationStore,
navigatorKey: navigatorKey,
authService: authService,
child: MaterialApp(
navigatorObservers: [routeObserver],
navigatorKey: navigatorKey,
debugShowCheckedModeBanner: false,
theme: settingsStore.theme,
+5 -3
View File
@@ -1,4 +1,5 @@
import 'dart:async';
import 'package:cake_wallet/reactions/fiat_rate_update.dart';
import 'package:cake_wallet/reactions/on_current_node_change.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/widgets.dart';
@@ -22,13 +23,14 @@ Future<void> bootstrap(GlobalKey<NavigatorState> navigatorKey) async {
final currentWalletName = getIt
.get<SharedPreferences>()
.getString(PreferencesKey.currentWalletName);
authenticationStore.state = currentWalletName == null
? AuthenticationState.denied
: AuthenticationState.installed;
if (currentWalletName != null) {
authenticationStore.state = AuthenticationState.installed;
}
startAuthenticationStateChange(authenticationStore, navigatorKey);
startCurrentWalletChangeReaction(
appStore, settingsStore, fiatConversionStore);
startCurrentFiatChangeReaction(appStore, settingsStore, fiatConversionStore);
startOnCurrentNodeChangeReaction(appStore);
startFiatRateUpdate(appStore, settingsStore, fiatConversionStore);
}
+19 -21
View File
@@ -1,5 +1,6 @@
import 'dart:async';
import 'package:cake_wallet/core/fiat_conversion_service.dart';
import 'package:cake_wallet/entities/fiat_api_mode.dart';
import 'package:cake_wallet/entities/update_haven_rate.dart';
import 'package:cake_wallet/store/app_store.dart';
import 'package:cake_wallet/store/dashboard/fiat_conversion_store.dart';
@@ -8,30 +9,27 @@ import 'package:cw_core/wallet_type.dart';
Timer? _timer;
Future<void> startFiatRateUpdate(AppStore appStore, SettingsStore settingsStore,
FiatConversionStore fiatConversionStore) async {
Future<void> startFiatRateUpdate(
AppStore appStore, SettingsStore settingsStore, FiatConversionStore fiatConversionStore) async {
if (_timer != null) {
return;
}
if (appStore.wallet != null) {
fiatConversionStore.prices[appStore.wallet!.currency] =
await FiatConversionService.fetchPrice(
appStore.wallet!.currency, settingsStore.fiatCurrency);
}
_timer = Timer.periodic(Duration(seconds: 30), (_) async {
try {
if (appStore.wallet == null || settingsStore.fiatApiMode == FiatApiMode.disabled) {
return;
}
_timer = Timer.periodic(
Duration(seconds: 30),
(_) async {
try {
if (appStore.wallet!.type == WalletType.haven) {
await updateHavenRate(fiatConversionStore);
} else {
fiatConversionStore.prices[appStore.wallet!.currency] = await FiatConversionService.fetchPrice(
appStore.wallet!.currency, settingsStore.fiatCurrency);
}
} catch(e) {
print(e);
}
});
if (appStore.wallet!.type == WalletType.haven) {
await updateHavenRate(fiatConversionStore);
} else {
fiatConversionStore.prices[appStore.wallet!.currency] =
await FiatConversionService.fetchPrice(
appStore.wallet!.currency, settingsStore.fiatCurrency);
}
} catch (e) {
print(e);
}
});
}
@@ -26,10 +26,5 @@ void startAuthenticationStateChange(AuthenticationStore authenticationStore,
await navigatorKey.currentState!.pushNamedAndRemoveUntil(Routes.dashboard, (route) => false);
return;
}
if (state == AuthenticationState.denied) {
await navigatorKey.currentState!.pushNamedAndRemoveUntil(Routes.welcome, (_) => false);
return;
}
});
}
+2 -1
View File
@@ -1,3 +1,4 @@
import 'package:cake_wallet/entities/fiat_api_mode.dart';
import 'package:mobx/mobx.dart';
import 'package:cake_wallet/core/fiat_conversion_service.dart';
import 'package:cake_wallet/store/dashboard/fiat_conversion_store.dart';
@@ -12,7 +13,7 @@ void startCurrentFiatChangeReaction(AppStore appStore,
_onCurrentFiatCurrencyChangeDisposer?.reaction.dispose();
_onCurrentFiatCurrencyChangeDisposer = reaction(
(_) => settingsStore.fiatCurrency, (FiatCurrency fiatCurrency) async {
if (appStore.wallet == null) {
if (appStore.wallet == null || settingsStore.fiatApiMode == FiatApiMode.disabled) {
return;
}
+2 -1
View File
@@ -1,3 +1,4 @@
import 'package:cake_wallet/entities/fiat_api_mode.dart';
import 'package:cake_wallet/entities/fiat_currency.dart';
import 'package:cake_wallet/entities/update_haven_rate.dart';
import 'package:cw_core/transaction_history.dart';
@@ -87,7 +88,7 @@ void startCurrentWalletChangeReaction(AppStore appStore,
TransactionHistoryBase<TransactionInfo>, TransactionInfo>?
wallet) async {
try {
if (wallet == null) {
if (wallet == null || settingsStore.fiatApiMode == FiatApiMode.disabled) {
return;
}
+46 -10
View File
@@ -5,21 +5,30 @@ import 'package:cake_wallet/src/screens/backup/edit_backup_password_page.dart';
import 'package:cake_wallet/src/screens/buy/buy_webview_page.dart';
import 'package:cake_wallet/src/screens/buy/onramper_page.dart';
import 'package:cake_wallet/src/screens/buy/pre_order_page.dart';
import 'package:cake_wallet/src/screens/settings/display_settings_page.dart';
import 'package:cake_wallet/src/screens/settings/other_settings_page.dart';
import 'package:cake_wallet/src/screens/settings/privacy_page.dart';
import 'package:cake_wallet/src/screens/settings/security_backup_page.dart';
import 'package:cake_wallet/src/screens/ionia/cards/ionia_account_cards_page.dart';
import 'package:cake_wallet/src/screens/ionia/cards/ionia_account_page.dart';
import 'package:cake_wallet/src/screens/ionia/cards/ionia_custom_redeem_page.dart';
import 'package:cake_wallet/src/screens/ionia/cards/ionia_custom_tip_page.dart';
import 'package:cake_wallet/src/screens/ionia/cards/ionia_gift_card_detail_page.dart';
import 'package:cake_wallet/src/screens/ionia/cards/ionia_more_options_page.dart';
import 'package:cake_wallet/src/screens/new_wallet/advanced_privacy_settings_page.dart';
import 'package:cake_wallet/src/screens/order_details/order_details_page.dart';
import 'package:cake_wallet/src/screens/pin_code/pin_code_widget.dart';
import 'package:cake_wallet/src/screens/restore/restore_from_backup_page.dart';
import 'package:cake_wallet/src/screens/restore/wallet_restore_page.dart';
import 'package:cake_wallet/src/screens/seed/pre_seed_page.dart';
import 'package:cake_wallet/src/screens/settings/connection_sync_page.dart';
import 'package:cake_wallet/src/screens/support/support_page.dart';
import 'package:cake_wallet/src/screens/unspent_coins/unspent_coins_details_page.dart';
import 'package:cake_wallet/src/screens/unspent_coins/unspent_coins_list_page.dart';
import 'package:cake_wallet/utils/payment_request.dart';
import 'package:cake_wallet/view_model/monero_account_list/account_list_item.dart';
import 'package:cake_wallet/view_model/node_list/node_create_or_edit_view_model.dart';
import 'package:cake_wallet/view_model/advanced_privacy_settings_view_model.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/routes.dart';
@@ -36,7 +45,6 @@ import 'package:cake_wallet/src/screens/dashboard/dashboard_page.dart';
import 'package:cake_wallet/src/screens/seed/wallet_seed_page.dart';
import 'package:cake_wallet/src/screens/auth/auth_page.dart';
import 'package:cake_wallet/src/screens/nodes/node_create_or_edit_page.dart';
import 'package:cake_wallet/src/screens/nodes/nodes_list_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';
@@ -56,7 +64,6 @@ import 'package:cake_wallet/src/screens/contact/contact_page.dart';
import 'package:cake_wallet/src/screens/wallet_keys/wallet_keys_page.dart';
import 'package:cake_wallet/src/screens/restore/restore_wallet_from_seed_details.dart';
import 'package:cake_wallet/src/screens/exchange/exchange_page.dart';
import 'package:cake_wallet/src/screens/settings/settings.dart';
import 'package:cake_wallet/src/screens/rescan/rescan_page.dart';
import 'package:cake_wallet/src/screens/faq/faq_page.dart';
import 'package:cake_wallet/src/screens/trade_details/trade_details_page.dart';
@@ -74,6 +81,7 @@ import 'package:cake_wallet/src/screens/ionia/ionia.dart';
import 'package:cake_wallet/src/screens/ionia/cards/ionia_payment_status_page.dart';
import 'package:cake_wallet/anypay/any_pay_payment_committed_info.dart';
import 'package:cake_wallet/ionia/ionia_any_pay_payment_info.dart';
import 'package:cw_core/crypto_currency.dart';
late RouteSettings currentRouteSettings;
@@ -209,8 +217,12 @@ Route<dynamic> createRoute(RouteSettings settings) {
builder: (_) => getIt.get<DashboardPage>());
case Routes.send:
final initialPaymentRequest = settings.arguments as PaymentRequest?;
return CupertinoPageRoute<void>(
fullscreenDialog: true, builder: (_) => getIt.get<SendPage>());
fullscreenDialog: true, builder: (_) => getIt.get<SendPage>(
param1: initialPaymentRequest,
));
case Routes.sendTemplate:
return CupertinoPageRoute<void>(
@@ -274,10 +286,26 @@ Route<dynamic> createRoute(RouteSettings settings) {
param2: false),
onWillPop: () async => false));
case Routes.nodeList:
case Routes.connectionSync:
return CupertinoPageRoute<void>(
builder: (_) => getIt.get<NodeListPage>());
builder: (_) => getIt.get<ConnectionSyncPage>());
case Routes.securityBackupPage:
return CupertinoPageRoute<void>(
builder: (_) => getIt.get<SecurityBackupPage>());
case Routes.privacyPage:
return CupertinoPageRoute<void>(
builder: (_) => getIt.get<PrivacyPage>());
case Routes.displaySettingsPage:
return CupertinoPageRoute<void>(
builder: (_) => getIt.get<DisplaySettingsPage>());
case Routes.otherSettingsPage:
return CupertinoPageRoute<void>(
builder: (_) => getIt.get<OtherSettingsPage>());
case Routes.newNode:
return CupertinoPageRoute<void>(
builder: (_) => getIt.get<NodeCreateOrEditPage>());
@@ -298,11 +326,13 @@ Route<dynamic> createRoute(RouteSettings settings) {
case Routes.addressBook:
return MaterialPageRoute<void>(
builder: (_) => getIt.get<ContactListPage>(param1: true));
builder: (_) =>
getIt.get<ContactListPage>());
case Routes.pickerAddressBook:
final selectedCurrency = settings.arguments as CryptoCurrency;
return MaterialPageRoute<void>(
builder: (_) => getIt.get<ContactListPage>(param1: false));
builder: (_) => getIt.get<ContactListPage>(param1: selectedCurrency));
case Routes.addressBookAddContact:
return CupertinoPageRoute<void>(
@@ -360,9 +390,6 @@ Route<dynamic> createRoute(RouteSettings settings) {
return CupertinoPageRoute<void>(
builder: (_) => getIt.get<ExchangeTemplatePage>());
case Routes.settings:
return MaterialPageRoute<void>(builder: (_) => getIt.get<SettingsPage>());
case Routes.rescan:
return MaterialPageRoute<void>(builder: (_) => getIt.get<RescanPage>());
@@ -475,6 +502,15 @@ Route<dynamic> createRoute(RouteSettings settings) {
case Routes.onramperPage:
return CupertinoPageRoute<void>(builder: (_) => getIt.get<OnRamperPage>());
case Routes.advancedPrivacySettings:
final type = settings.arguments as WalletType;
return CupertinoPageRoute<void>(
builder: (_) => AdvancedPrivacySettingsPage(
getIt.get<AdvancedPrivacySettingsViewModel>(param1: type),
getIt.get<NodeCreateOrEditViewModel>(param1: type),
));
default:
return MaterialPageRoute<void>(
builder: (_) => Scaffold(
+6 -1
View File
@@ -21,7 +21,6 @@ class Routes {
static const seedLanguage = '/seed_language';
static const walletList = '/view_model.wallet_list';
static const auth = '/auth';
static const nodeList = '/node_list';
static const newNode = '/new_node_list';
static const login = '/login';
static const splash = '/splash';
@@ -77,4 +76,10 @@ class Routes {
static const ioniaMoreOptionsPage = '/ionia_more_options_page';
static const ioniaCustomRedeemPage = '/ionia_custom_redeem_page';
static const onramperPage = '/onramper';
static const connectionSync = '/connection_sync_page';
static const securityBackupPage = '/security_and_backup_page';
static const privacyPage = '/privacy_page';
static const displaySettingsPage = '/display_settings_page';
static const otherSettingsPage = '/other_settings_page';
static const advancedPrivacySettings = '/advanced_privacy_settings';
}
+26 -88
View File
@@ -9,24 +9,22 @@ import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:flutter_slidable/flutter_slidable.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
import 'package:cake_wallet/view_model/contact_list/contact_list_view_model.dart';
import 'package:cake_wallet/src/widgets/collapsible_standart_list.dart';
class ContactListPage extends BasePage {
ContactListPage(this.contactListViewModel, {this.isEditable = true});
ContactListPage(this.contactListViewModel);
final ContactListViewModel contactListViewModel;
final bool isEditable;
@override
String get title => S.current.address_book;
@override
Widget? trailing(BuildContext context) {
if (!isEditable) {
if (!contactListViewModel.isEditable) {
return null;
}
@@ -60,21 +58,24 @@ class ContactListPage extends BasePage {
@override
Widget body(BuildContext context) {
return Container(
padding: EdgeInsets.only(top: 20.0, bottom: 20.0),
child: Observer(
builder: (_) {
return CollapsibleSectionList(
builder: (_) {
final contacts = contactListViewModel.contactsToShow;
final walletContacts = contactListViewModel.walletContactsToShow;
return CollapsibleSectionList(
context: context,
sectionCount: 2,
themeColor: Theme.of(context).primaryTextTheme.headline6!.color!,
dividerThemeColor:
Theme.of(context).primaryTextTheme.caption!.decorationColor!,
sectionTitleBuilder: (_, int sectionIndex) {
var title = 'Contacts';
var title = S.current.contact_list_contacts;
if (sectionIndex == 0) {
title = 'My wallets';
title = S.current.contact_list_wallets;
}
return Container(
@@ -82,35 +83,37 @@ class ContactListPage extends BasePage {
child: Text(title, style: TextStyle(fontSize: 36)));
},
itemCounter: (int sectionIndex) => sectionIndex == 0
? contactListViewModel.walletContacts.length
: contactListViewModel.contacts.length,
? walletContacts.length
: contacts.length,
itemBuilder: (_, sectionIndex, index) {
if (sectionIndex == 0) {
final walletInfo = contactListViewModel.walletContacts[index];
final walletInfo = walletContacts[index];
return generateRaw(context, walletInfo);
}
final contact = contactListViewModel.contacts[index];
final contact = contacts[index];
final content = generateRaw(context, contact);
return !isEditable
? content
: Slidable(
return contactListViewModel.isEditable
? Slidable(
key: Key('${contact.key}'),
endActionPane: _actionPane(context, contact),
child: content,
);
)
: content;
},
);
},
));
);})
);
}
Widget generateRaw(BuildContext context, ContactBase contact) {
final image = _getCurrencyImage(contact.type);
final image = contact.type.iconPath;
final currencyIcon = image != null ? Image.asset(image, height: 24, width: 24)
: const SizedBox(height: 24, width: 24);
return GestureDetector(
onTap: () async {
if (!isEditable) {
if (!contactListViewModel.isEditable) {
Navigator.of(context).pop(contact);
return;
}
@@ -131,12 +134,10 @@ class ContactListPage extends BasePage {
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
image ?? Offstage(),
currencyIcon,
Expanded(
child: Padding(
padding: image != null
? EdgeInsets.only(left: 12)
: EdgeInsets.only(left: 0),
padding: EdgeInsets.only(left: 12),
child: Text(
contact.name,
style: TextStyle(
@@ -152,69 +153,6 @@ class ContactListPage extends BasePage {
);
}
Image? _getCurrencyImage(CryptoCurrency currency) {
Image? image;
switch (currency) {
case CryptoCurrency.xmr:
image =
Image.asset('assets/images/monero_logo.png', height: 24, width: 24);
break;
case CryptoCurrency.ada:
image = Image.asset('assets/images/ada.png', height: 24, width: 24);
break;
case CryptoCurrency.bch:
image = Image.asset('assets/images/bch.png', height: 24, width: 24);
break;
case CryptoCurrency.bnb:
image = Image.asset('assets/images/bnb.png', height: 24, width: 24);
break;
case CryptoCurrency.btc:
image = Image.asset('assets/images/bitcoin.png', height: 24, width: 24);
break;
case CryptoCurrency.dai:
image = Image.asset('assets/images/dai.png', height: 24, width: 24);
break;
case CryptoCurrency.dash:
image = Image.asset('assets/images/dash.png', height: 24, width: 24);
break;
case CryptoCurrency.eos:
image = Image.asset('assets/images/eos.png', height: 24, width: 24);
break;
case CryptoCurrency.eth:
image = Image.asset('assets/images/eth.png', height: 24, width: 24);
break;
case CryptoCurrency.ltc:
image =
Image.asset('assets/images/litecoin.png', height: 24, width: 24);
break;
case CryptoCurrency.nano:
image = Image.asset('assets/images/nano.png', height: 24, width: 24);
break;
case CryptoCurrency.trx:
image = Image.asset('assets/images/trx.png', height: 24, width: 24);
break;
case CryptoCurrency.usdt:
image = Image.asset('assets/images/usdt.png', height: 24, width: 24);
break;
case CryptoCurrency.usdterc20:
image = Image.asset('assets/images/usdterc.png', height: 24, width: 24);
break;
case CryptoCurrency.xlm:
image = Image.asset('assets/images/xlm.png', height: 24, width: 24);
break;
case CryptoCurrency.xrp:
image = Image.asset('assets/images/xrp.png', height: 24, width: 24);
break;
case CryptoCurrency.xhv:
image = Image.asset('assets/images/haven_logo.png', height: 24, width: 24);
break;
default:
image = null;
}
return image;
}
Future<bool> showAlertDialog(BuildContext context) async {
return await showPopUp<bool>(
context: context,
@@ -60,7 +60,7 @@ class DashboardPage extends BasePage {
Widget middle(BuildContext context) {
return SyncIndicator(dashboardViewModel: walletViewModel,
onTap: () => Navigator.of(context, rootNavigator: true)
.pushNamed(Routes.nodeList));
.pushNamed(Routes.connectionSync));
}
@override
+51 -85
View File
@@ -1,81 +1,64 @@
import 'package:cake_wallet/palette.dart';
import 'package:cake_wallet/src/screens/dashboard/wallet_menu_item.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/screens/auth/auth_page.dart';
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
import 'package:cake_wallet/wallet_type_utils.dart';
// FIXME: terrible design
class WalletMenu {
WalletMenu(this.context, this.reconnect, this.hasRescan) : items = [] {
items.addAll([
WalletMenuItem(
title: S.current.reconnect,
image: Image.asset('assets/images/reconnect_menu.png',
height: 16, width: 16),
handler: () => _presentReconnectAlert(context)),
if (hasRescan)
WalletMenuItem(
title: S.current.rescan,
image: Image.asset('assets/images/filter_icon.png',
height: 16, width: 16, color: Palette.darkBlue),
handler: () => Navigator.of(context).pushNamed(Routes.rescan)),
WalletMenuItem(
title: S.current.wallets,
image: Image.asset('assets/images/wallet_menu.png',
height: 16, width: 16),
handler: () => Navigator.of(context).pushNamed(Routes.walletList)),
WalletMenuItem(
title: S.current.nodes,
image: Image.asset('assets/images/nodes_menu.png',
height: 16, width: 16),
handler: () => Navigator.of(context).pushNamed(Routes.nodeList)),
WalletMenuItem(
title: S.current.show_keys,
image:
Image.asset('assets/images/key_menu.png', height: 16, width: 16),
handler: () {
Navigator.of(context).pushNamed(Routes.auth,
arguments: (bool isAuthenticatedSuccessfully, AuthPageState auth) {
if (isAuthenticatedSuccessfully) {
auth.close(route: Routes.showKeys);
}
});
}),
WalletMenuItem(
title: S.current.address_book_menu,
image: Image.asset('assets/images/open_book_menu.png',
height: 16, width: 16),
handler: () => Navigator.of(context).pushNamed(Routes.addressBook)),
WalletMenuItem(
title: S.current.backup,
image: Image.asset('assets/images/restore_wallet.png',
height: 16,
width: 16,
color: Palette.darkBlue),
handler: () {
Navigator.of(context).pushNamed(
Routes.auth,
arguments: (bool isAuthenticatedSuccessfully, AuthPageState auth) {
if (isAuthenticatedSuccessfully) {
auth.close(route:Routes.backup);
}
});
}),
WalletMenuItem(
title: S.current.settings_title,
image: Image.asset('assets/images/settings_menu.png',
height: 16, width: 16),
handler: () => Navigator.of(context).pushNamed(Routes.settings)),
WalletMenuItem(
title: S.current.settings_support,
image: Image.asset('assets/images/question_mark.png',
height: 16, width: 16, color: Palette.darkBlue),
handler: () => Navigator.of(context).pushNamed(Routes.support)),
WalletMenuItem(
title: S.current.connection_sync,
image: Image.asset('assets/images/nodes_menu.png',
height: 16, width: 16),
handler: () => Navigator.of(context).pushNamed(Routes.connectionSync),
),
WalletMenuItem(
title: S.current.wallets,
image: Image.asset('assets/images/wallet_menu.png',
height: 16, width: 16),
handler: () => Navigator.of(context).pushNamed(Routes.walletList),
),
WalletMenuItem(
title: S.current.address_book_menu,
image: Image.asset('assets/images/open_book_menu.png',
height: 16, width: 16),
handler: () => Navigator.of(context).pushNamed(Routes.addressBook),
),
WalletMenuItem(
title: S.current.security_and_backup,
image:
Image.asset('assets/images/key_menu.png', height: 16, width: 16),
handler: () {
Navigator.of(context).pushNamed(Routes.securityBackupPage);
}),
WalletMenuItem(
title: S.current.privacy_settings,
image:
Image.asset('assets/images/privacy_menu.png', height: 16, width: 16),
handler: () {
Navigator.of(context).pushNamed(Routes.privacyPage);
}),
WalletMenuItem(
title: S.current.display_settings,
image: Image.asset('assets/images/eye_menu.png',
height: 16, width: 16),
handler: () => Navigator.of(context).pushNamed(Routes.displaySettingsPage),
),
WalletMenuItem(
title: S.current.other_settings,
image: Image.asset('assets/images/settings_menu.png',
height: 16, width: 16),
handler: () => Navigator.of(context).pushNamed(Routes.otherSettingsPage),
),
WalletMenuItem(
title: S.current.settings_support,
image: Image.asset('assets/images/question_mark.png',
height: 16, width: 16, color: Palette.darkBlue),
handler: () => Navigator.of(context).pushNamed(Routes.support),
),
]);
}
@@ -86,23 +69,6 @@ class WalletMenu {
void action(int index) {
final item = items[index];
item?.handler();
}
Future<void> _presentReconnectAlert(BuildContext context) async {
await showPopUp<void>(
context: context,
builder: (BuildContext context) {
return AlertWithTwoActions(
alertTitle: S.of(context).reconnection,
alertContent: S.of(context).reconnect_alert_text,
rightButtonText: S.of(context).ok,
leftButtonText: S.of(context).cancel,
actionRightButton: () async {
Navigator.of(context).pop();
await reconnect?.call();
},
actionLeftButton: () => Navigator.of(context).pop());
});
item.handler();
}
}
@@ -9,12 +9,7 @@ class FilterTile extends StatelessWidget {
Widget build(BuildContext context) {
return Container(
width: double.infinity,
padding: EdgeInsets.only(
top: 18,
bottom: 18,
left: 24,
right: 24
),
padding: EdgeInsets.symmetric(vertical: 8.0, horizontal: 24.0),
child: child,
);
}
@@ -1,25 +1,26 @@
import 'dart:ui';
import 'package:cake_wallet/palette.dart';
import 'package:cake_wallet/src/screens/dashboard/widgets/filter_tile.dart';
import 'package:cake_wallet/src/widgets/section_divider.dart';
import 'package:cake_wallet/src/widgets/standard_checkbox.dart';
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/src/widgets/alert_background.dart';
import 'package:cake_wallet/src/widgets/alert_close_button.dart';
import 'package:cake_wallet/src/widgets/checkbox_widget.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
//import 'package:date_range_picker/date_range_picker.dart' as date_rage_picker;
class FilterWidget extends StatelessWidget {
FilterWidget({required this.dashboardViewModel});
final DashboardViewModel dashboardViewModel;
final backVector = Image.asset('assets/images/back_vector.png',
color: Palette.darkBlueCraiola
);
final closeIcon = Image.asset('assets/images/close.png', color: Palette.darkBlueCraiola);
@override
Widget build(BuildContext context) {
const sectionDivider = const SectionDivider();
return AlertBackground(
child: Stack(
alignment: Alignment.center,
@@ -27,129 +28,82 @@ class FilterWidget extends StatelessWidget {
Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text(
S.of(context).filters,
style: TextStyle(
color: Colors.white,
fontSize: 18,
fontWeight: FontWeight.bold,
fontFamily: 'Lato',
decoration: TextDecoration.none,
),
),
Padding(
padding: EdgeInsets.only(
left: 24,
right: 24,
top: 24
),
padding: EdgeInsets.only(left: 24, right: 24, top: 24),
child: ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(14)),
borderRadius: BorderRadius.all(Radius.circular(24)),
child: Container(
color: Theme.of(context).textTheme!.bodyText1!.decorationColor!,
child: ListView.separated(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: dashboardViewModel.filterItems.length,
separatorBuilder: (context, _) => Container(
height: 1,
color: Theme.of(context).accentTextTheme!.subtitle1!.backgroundColor!,
child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
Padding(
padding: EdgeInsets.all(24.0),
child: Text(
S.of(context).filter_by,
style: TextStyle(
color: Theme.of(context).primaryTextTheme.overline!.color!,
fontSize: 16,
fontFamily: 'Lato',
decoration: TextDecoration.none,
),
),
),
itemBuilder: (_, index1) {
final title = dashboardViewModel.filterItems.keys.elementAt(index1);
final section = dashboardViewModel.filterItems.values.elementAt(index1);
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Padding(
padding: EdgeInsets.only(
top: 20,
left: 24,
right: 24
),
child: Text(
title,
style: TextStyle(
color: Theme.of(context).accentTextTheme!.subtitle1!.color!,
fontSize: 16,
fontWeight: FontWeight.w500,
fontFamily: 'Lato',
decoration: TextDecoration.none
sectionDivider,
ListView.separated(
padding: EdgeInsets.zero,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: dashboardViewModel.filterItems.length,
separatorBuilder: (context, _) => sectionDivider,
itemBuilder: (_, index1) {
final title = dashboardViewModel.filterItems.keys.elementAt(index1);
final section = dashboardViewModel.filterItems.values.elementAt(index1);
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Padding(
padding: EdgeInsets.only(top: 20, left: 24, right: 24),
child: Text(
title,
style: TextStyle(
color: Theme.of(context).primaryTextTheme!.headline6!.color!,
fontSize: 16,
fontFamily: 'Lato',
fontWeight: FontWeight.bold,
decoration: TextDecoration.none),
),
),
),
ListView.separated(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: section.length,
separatorBuilder: (context, _) => Container(
height: 1,
padding: EdgeInsets.only(left: 24),
color: Theme.of(context).textTheme!.bodyText1!.decorationColor!,
child: Container(
height: 1,
color: Theme.of(context).accentTextTheme!.subtitle1!.backgroundColor!,
),
),
itemBuilder: (_, index2) {
final item = section[index2];
final content = item.onChanged != null
? CheckboxWidget(
value: item.value(),
caption: item.caption,
onChanged: item.onChanged
)
: GestureDetector(
onTap: () async {
//final List<DateTime> picked =
//await date_rage_picker.showDatePicker(
// context: context,
// initialFirstDate: DateTime.now()
// .subtract(Duration(days: 1)),
// initialLastDate: (DateTime.now()),
// firstDate: DateTime(2015),
// lastDate: DateTime.now()
// .add(Duration(days: 1)));
//if (picked != null && picked.length == 2) {
// dashboardViewModel.transactionFilterStore
// .changeStartDate(picked.first);
// dashboardViewModel.transactionFilterStore
// .changeEndDate(picked.last);
//}
},
child: Padding(
padding: EdgeInsets.only(left: 32),
child: Text(
item.caption,
style: TextStyle(
color: Theme.of(context).primaryTextTheme!.headline6!.color!,
fontSize: 18,
fontFamily: 'Lato',
fontWeight: FontWeight.w500,
decoration: TextDecoration.none
),
),
),
);
return FilterTile(child: content);
},
)
],
);
},
),
ListView.builder(
padding: EdgeInsets.symmetric(vertical: 8.0),
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: section.length,
itemBuilder: (_, index2) {
final item = section[index2];
final content = Observer(
builder: (_) => StandardCheckbox(
value: item.value(),
caption: item.caption,
gradientBackground: true,
borderColor: Theme.of(context).dividerColor,
iconColor: Colors.white,
onChanged: (value) => item.onChanged(),
));
return FilterTile(child: content);
},
)
],
);
},
),
]),
),
),
),
],
),
AlertCloseButton(image: backVector)
AlertCloseButton(image: closeIcon)
],
),
);
}
}
}
@@ -6,6 +6,7 @@ import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:url_launcher/url_launcher.dart';
class MarketPlacePage extends StatelessWidget {
@@ -48,6 +49,15 @@ class MarketPlacePage extends StatelessWidget {
title: S.of(context).cake_pay_title,
subTitle: S.of(context).cake_pay_subtitle,
),
SizedBox(height: 20),
MarketPlaceItem(
onTap: () => launchUrl(
Uri.https("buy.cakepay.com"),
mode: LaunchMode.externalApplication,
),
title: S.of(context).cake_pay_web_cards_title,
subTitle: S.of(context).cake_pay_web_cards_subtitle,
),
],
),
),
@@ -72,7 +82,7 @@ class MarketPlacePage extends StatelessWidget {
buttonAction: () => Navigator.of(context).pop());
});
break;
default:
default:
Navigator.of(context).pushNamed(Routes.ioniaWelcomePage);
}
}
@@ -57,7 +57,9 @@ class TransactionsPage extends StatelessWidget {
formattedDate: DateFormat('HH:mm')
.format(transaction.date),
formattedAmount: item.formattedCryptoAmount,
formattedFiatAmount: item.formattedFiatAmount,
formattedFiatAmount:
dashboardViewModel.balanceViewModel.isFiatDisabled
? '' : item.formattedFiatAmount,
isPending: transaction.isPending));
}
@@ -40,7 +40,6 @@ class ExchangePage extends BasePage {
final ExchangeViewModel exchangeViewModel;
final depositKey = GlobalKey<ExchangeCardState>();
final receiveKey = GlobalKey<ExchangeCardState>();
final checkBoxKey = GlobalKey<StandardCheckboxState>();
final _formKey = GlobalKey<FormState>();
final _depositAmountFocus = FocusNode();
final _depositAddressFocus = FocusNode();
@@ -339,7 +338,6 @@ class ExchangePage extends BasePage {
mainAxisAlignment: MainAxisAlignment.start,
children: [
StandardCheckbox(
key: checkBoxKey,
value: exchangeViewModel.isFixedRateMode,
caption: S.of(context).fixed_rate,
onChanged: (value) =>
@@ -682,12 +680,6 @@ class ExchangePage extends BasePage {
}
});
reaction((_) => exchangeViewModel.isFixedRateMode, (bool value) {
if (checkBoxKey.currentState!.value != exchangeViewModel.isFixedRateMode) {
checkBoxKey.currentState!.value = exchangeViewModel.isFixedRateMode;
}
});
depositAddressController.addListener(
() => exchangeViewModel.depositAddress = depositAddressController.text);
@@ -56,7 +56,7 @@ class CurrencyPickerState extends State<CurrencyPicker> {
.where((element) =>
(element.title != null ? element.title.toLowerCase().contains(subString.toLowerCase()) : false) ||
(element.tag != null ? element.tag!.toLowerCase().contains(subString.toLowerCase()) : false) ||
(element.name != null ? element.name!.toLowerCase().contains(subString.toLowerCase()) : false))
(element.fullName != null ? element.fullName!.toLowerCase().contains(subString.toLowerCase()) : false))
.toList();
return;
}
@@ -395,7 +395,8 @@ class ExchangeCardState extends State<ExchangeCard> {
buttonColor: widget.addressButtonsColor,
validator: widget.addressTextFieldValidator,
onPushPasteButton: widget.onPushPasteButton,
onPushAddressBookButton: widget.onPushAddressBookButton
onPushAddressBookButton: widget.onPushAddressBookButton,
selectedCurrency: _selectedCurrency
),
)
@@ -8,7 +8,7 @@ import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/core/execution_state.dart';
import 'package:cake_wallet/src/screens/exchange_trade/information_page.dart';
import 'package:cake_wallet/src/screens/send/widgets/confirm_sending_alert.dart';
import 'package:cake_wallet/src/widgets/standart_list_row.dart';
import 'package:cake_wallet/src/widgets/list_row.dart';
import 'package:cake_wallet/utils/show_bar.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/exchange/exchange_trade_view_model.dart';
@@ -194,7 +194,7 @@ class ExchangeTradeState extends State<ExchangeTradeForm> {
final item = widget.exchangeTradeViewModel.items[index];
final value = item.data ?? fetchingLabel;
final content = StandartListRow(
final content = ListRow(
title: item.title,
value: value,
valueFontSize: 14,
@@ -378,12 +378,10 @@ class ExchangeTradeState extends State<ExchangeTradeForm> {
});
},
actionLeftButton: () => Navigator.of(context).pop(),
feeFiatAmount: widget.exchangeTradeViewModel.sendViewModel.pendingTransactionFeeFiatAmount
+ ' ' + widget.exchangeTradeViewModel.sendViewModel.fiat.title,
fiatAmountValue: widget.exchangeTradeViewModel.sendViewModel
.pendingTransactionFiatAmount +
' ' +
widget.exchangeTradeViewModel.sendViewModel.fiat.title,
feeFiatAmount: widget.exchangeTradeViewModel
.pendingTransactionFeeFiatAmountFormatted,
fiatAmountValue: widget.exchangeTradeViewModel
.pendingTransactionFiatAmountValueFormatted,
outputs: widget.exchangeTradeViewModel.sendViewModel
.outputs);
});
@@ -1,3 +1,4 @@
import 'package:cake_wallet/core/execution_state.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/screens/ionia/widgets/card_item.dart';
import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
@@ -18,12 +19,11 @@ class IoniaCustomRedeemPage extends BasePage {
) : _amountFieldFocus = FocusNode(),
_amountController = TextEditingController() {
_amountController.addListener(() {
ioniaCustomRedeemViewModel.updateAmount(_amountController.text);
ioniaCustomRedeemViewModel.updateAmount(_amountController.text);
});
}
final IoniaCustomRedeemViewModel ioniaCustomRedeemViewModel;
@override
String get title => S.current.custom_redeem_amount;
@@ -50,7 +50,7 @@ class IoniaCustomRedeemPage extends BasePage {
disableScroll: true,
config: KeyboardActionsConfig(
keyboardActionsPlatform: KeyboardActionsPlatform.IOS,
keyboardBarColor: Theme.of(context).accentTextTheme!.bodyText1!.backgroundColor!,
keyboardBarColor: Theme.of(context).accentTextTheme.bodyText1!.backgroundColor!,
nextFocus: false,
actions: [
KeyboardActionsItem(
@@ -67,10 +67,11 @@ class IoniaCustomRedeemPage extends BasePage {
Container(
padding: EdgeInsets.symmetric(horizontal: 25),
decoration: BoxDecoration(
borderRadius: BorderRadius.only(bottomLeft: Radius.circular(24), bottomRight: Radius.circular(24)),
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(24), bottomRight: Radius.circular(24)),
gradient: LinearGradient(colors: [
Theme.of(context).primaryTextTheme!.subtitle1!.color!,
Theme.of(context).primaryTextTheme!.subtitle1!.decorationColor!,
Theme.of(context).primaryTextTheme.subtitle1!.color!,
Theme.of(context).primaryTextTheme.subtitle1!.decorationColor!,
], begin: Alignment.topLeft, end: Alignment.bottomRight),
),
child: Column(
@@ -85,11 +86,11 @@ class IoniaCustomRedeemPage extends BasePage {
inputFormatters: [FilteringTextInputFormatter.deny(RegExp('[\-|\ ]'))],
hintText: '1000',
placeholderTextStyle: TextStyle(
color: Theme.of(context).primaryTextTheme!.headline5!.color!,
color: Theme.of(context).primaryTextTheme.headline5!.color!,
fontWeight: FontWeight.w500,
fontSize: 36,
),
borderColor: Theme.of(context).primaryTextTheme!.headline5!.color!,
borderColor: Theme.of(context).primaryTextTheme.headline5!.color!,
textColor: Colors.white,
textStyle: TextStyle(
color: Colors.white,
@@ -114,14 +115,17 @@ class IoniaCustomRedeemPage extends BasePage {
),
),
SizedBox(height: 8),
Observer(builder: (_)=>
!ioniaCustomRedeemViewModel.disableRedeem ?
Center(
child: Text('\$${giftCard.remainingAmount} - \$${ioniaCustomRedeemViewModel.amount} = \$${ioniaCustomRedeemViewModel.formattedRemaining} ${S.of(context).remaining}',
style: TextStyle(
color: Theme.of(context).primaryTextTheme!.headline5!.color!,
),),
) : SizedBox.shrink(),
Observer(
builder: (_) => !ioniaCustomRedeemViewModel.disableRedeem
? Center(
child: Text(
'\$${giftCard.remainingAmount} - \$${ioniaCustomRedeemViewModel.amount} = \$${ioniaCustomRedeemViewModel.formattedRemaining} ${S.of(context).remaining}',
style: TextStyle(
color: Theme.of(context).primaryTextTheme.headline5!.color!,
),
),
)
: SizedBox.shrink(),
),
SizedBox(height: 24),
],
@@ -131,30 +135,37 @@ class IoniaCustomRedeemPage extends BasePage {
padding: const EdgeInsets.all(24.0),
child: CardItem(
title: giftCard.legalName,
backgroundColor: Theme.of(context).accentTextTheme!.headline1!.backgroundColor!.withOpacity(0.1),
backgroundColor: Theme.of(context)
.accentTextTheme
.headline1!
.backgroundColor!
.withOpacity(0.1),
discount: giftCard.remainingAmount,
isAmount: true,
discountBackground: AssetImage('assets/images/red_badge_discount.png'),
titleColor: Theme.of(context).accentTextTheme!.headline1!.backgroundColor!,
titleColor: Theme.of(context).accentTextTheme.headline1!.backgroundColor!,
subtitleColor: Theme.of(context).hintColor,
subTitle: S.of(context).online,
logoUrl: giftCard.logoUrl,
),
),
),
],
),
bottomSection: Column(
children: [
Padding(
padding: EdgeInsets.only(bottom: 12),
child: PrimaryButton(
onPressed: () {
Navigator.of(context).pop(_amountController.text);
},
isDisabled: ioniaCustomRedeemViewModel.disableRedeem,
text: S.of(context).add_custom_redemption,
color: Theme.of(context).accentTextTheme!.bodyText1!.color!,
textColor: Colors.white,
Observer(
builder: (_) => Padding(
padding: EdgeInsets.only(bottom: 12),
child: LoadingPrimaryButton(
isLoading: ioniaCustomRedeemViewModel.redeemState is IsExecutingState,
isDisabled: ioniaCustomRedeemViewModel.disableRedeem,
text: S.of(context).add_custom_redemption,
color: Theme.of(context).accentTextTheme.bodyText1!.color!,
textColor: Colors.white,
onPressed: () => ioniaCustomRedeemViewModel.addCustomRedeem().then((value) {
Navigator.of(context).pop(ioniaCustomRedeemViewModel.remaining.toString());
}),
),
),
),
SizedBox(height: 30),
@@ -11,6 +11,7 @@ import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
import 'package:cake_wallet/typography.dart';
import 'package:cake_wallet/utils/show_bar.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/utils/route_aware.dart';
import 'package:cake_wallet/view_model/ionia/ionia_gift_card_details_view_model.dart';
import 'package:device_display_brightness/device_display_brightness.dart';
import 'package:flutter/material.dart';
@@ -32,7 +33,7 @@ class IoniaGiftCardDetailPage extends BasePage {
final _backButton = Icon(
Icons.arrow_back_ios,
color: Theme.of(context).primaryTextTheme!.headline6!.color!,
color: Theme.of(context).primaryTextTheme.headline6!.color!,
size: 16,
);
return Padding(
@@ -43,14 +44,11 @@ class IoniaGiftCardDetailPage extends BasePage {
child: ButtonTheme(
minWidth: double.minPositive,
child: TextButton(
// FIX-ME: Style
// FIX-ME: Style
//highlightColor: Colors.transparent,
//splashColor: Colors.transparent,
//padding: EdgeInsets.all(0),
onPressed: () {
onClose(context);
DeviceDisplayBrightness.setBrightness(viewModel.brightness);
},
onPressed: ()=> onClose(context),
child: _backButton),
),
),
@@ -61,13 +59,13 @@ class IoniaGiftCardDetailPage extends BasePage {
Widget middle(BuildContext context) {
return Text(
viewModel.giftCard.legalName,
style: textMediumSemiBold(color: Theme.of(context).accentTextTheme!.headline1!.backgroundColor!),
style:
textMediumSemiBold(color: Theme.of(context).accentTextTheme.headline1!.backgroundColor!),
);
}
@override
Widget body(BuildContext context) {
viewModel.increaseBrightness();
reaction((_) => viewModel.redeemState, (ExecutionState state) {
if (state is FailureState) {
WidgetsBinding.instance.addPostFrameCallback((_) {
@@ -84,7 +82,12 @@ class IoniaGiftCardDetailPage extends BasePage {
}
});
return ScrollableWithBottomSection(
return RouteAwareWidget(
pushToWidget: ()=> viewModel.increaseBrightness(),
pushToNextWidget: ()=> DeviceDisplayBrightness.setBrightness(viewModel.brightness),
popNextWidget: ()=> viewModel.increaseBrightness(),
popWidget: ()=> DeviceDisplayBrightness.setBrightness(viewModel.brightness),
child: ScrollableWithBottomSection(
contentPadding: EdgeInsets.all(24),
content: Column(
children: [
@@ -102,20 +105,21 @@ class IoniaGiftCardDetailPage extends BasePage {
title: S.of(context).gift_card_number,
subTitle: viewModel.giftCard.cardNumber,
),
if (viewModel.giftCard.cardPin?.isNotEmpty ?? false)
...[Divider(height: 30),
if (viewModel.giftCard.cardPin.isNotEmpty) ...[
Divider(height: 30),
buildIoniaTile(
context,
title: S.of(context).pin_number,
subTitle: viewModel.giftCard.cardPin,
)],
)
],
Divider(height: 30),
Observer(builder: (_) =>
buildIoniaTile(
context,
title: S.of(context).amount,
subTitle: viewModel.remainingAmount.toStringAsFixed(2) ?? '0.00',
)),
Observer(
builder: (_) => buildIoniaTile(
context,
title: S.of(context).amount,
subTitle: viewModel.remainingAmount.toStringAsFixed(2),
)),
Divider(height: 50),
TextIconButton(
label: S.of(context).how_to_use_card,
@@ -130,29 +134,28 @@ class IoniaGiftCardDetailPage extends BasePage {
if (!viewModel.giftCard.isEmpty) {
return Column(
children: [
//PrimaryButton(
// onPressed: () async {
// final amount = await Navigator.of(context)
// .pushNamed(Routes.ioniaMoreOptionsPage, arguments: [viewModel.giftCard]) as String;
// if (amount != null) {
// viewModel.updateRemaining(double.parse(amount));
// }
// },
// text: S.of(context).more_options,
// color: Theme.of(context).accentTextTheme!.caption!.color!,
// textColor: Theme.of(context).primaryTextTheme!.headline6!.color!,
//),
//SizedBox(height: 12),
PrimaryButton(
onPressed: () async {
await Navigator.of(context).pushNamed(
Routes.ioniaMoreOptionsPage,
arguments: [viewModel.giftCard]) as String?;
viewModel.refeshCard();
},
text: S.of(context).more_options,
color: Theme.of(context).accentTextTheme.caption!.color!,
textColor: Theme.of(context).primaryTextTheme.headline6!.color!,
),
SizedBox(height: 12),
LoadingPrimaryButton(
isLoading: viewModel.redeemState is IsExecutingState,
onPressed: () => viewModel.redeem().then(
(_) {
Navigator.of(context)
.pushNamedAndRemoveUntil(Routes.ioniaManageCardsPage, (route) => route.isFirst);
Navigator.of(context).pushNamedAndRemoveUntil(
Routes.ioniaManageCardsPage, (route) => route.isFirst);
},
),
text: S.of(context).mark_as_redeemed,
color: Theme.of(context).accentTextTheme!.bodyText1!.color!,
color: Theme.of(context).accentTextTheme.bodyText1!.color!,
textColor: Colors.white,
),
],
@@ -163,17 +166,16 @@ class IoniaGiftCardDetailPage extends BasePage {
},
),
),
);
));
}
Widget buildIoniaTile(BuildContext context, {required String title, required String subTitle}) {
return IoniaTile(
title: title,
subTitle: subTitle,
onTap: () {
Clipboard.setData(ClipboardData(text: subTitle));
showBar<void>(context,
S.of(context).transaction_details_copied(title));
title: title,
subTitle: subTitle,
onTap: () {
Clipboard.setData(ClipboardData(text: subTitle));
showBar<void>(context, S.of(context).transaction_details_copied(title));
});
}
@@ -184,10 +186,10 @@ class IoniaGiftCardDetailPage extends BasePage {
showPopUp<void>(
context: context,
builder: (BuildContext context) {
return IoniaAlertModal(
return IoniaAlertModal(
title: S.of(context).how_to_use_card,
content: Column(
crossAxisAlignment: CrossAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: viewModel.giftCard.instructions
.map((instruction) {
return [
@@ -196,13 +198,13 @@ class IoniaGiftCardDetailPage extends BasePage {
child: Text(
instruction.header,
style: textLargeSemiBold(
color: Theme.of(context).textTheme!.headline3!.color!,
color: Theme.of(context).textTheme.headline3!.color!,
),
)),
Text(
instruction.body,
style: textMedium(
color: Theme.of(context).textTheme!.headline3!.color!,
color: Theme.of(context).textTheme.headline3!.color!,
),
)
];
@@ -210,7 +212,7 @@ class IoniaGiftCardDetailPage extends BasePage {
.expand((e) => e)
.toList()),
actionTitle: S.of(context).send_got_it,
);
);
});
}
}
@@ -118,7 +118,7 @@ class IoniaManageCardsPage extends BasePage {
width: 32,
padding: EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.15),
color: Theme.of(context).textTheme!.headline6!.backgroundColor!,
border: Border.all(
color: Colors.white.withOpacity(0.2),
),
@@ -5,7 +5,6 @@ import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/typography.dart';
import 'package:flutter/material.dart';
class IoniaMoreOptionsPage extends BasePage {
IoniaMoreOptionsPage(this.giftCard);
@@ -16,7 +15,7 @@ class IoniaMoreOptionsPage extends BasePage {
return Text(
S.current.more_options,
style: textMediumSemiBold(
color: Theme.of(context).accentTextTheme!.headline1!.backgroundColor!,
color: Theme.of(context).accentTextTheme.headline1!.backgroundColor!,
),
);
}
@@ -27,40 +26,45 @@ class IoniaMoreOptionsPage extends BasePage {
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
SizedBox(height: 10,),
Center(child: Text(S.of(context).choose_from_available_options, style: textMedium(
color: Theme.of(context).primaryTextTheme!.headline6!.color!,
),)),
SizedBox(height: 40,),
InkWell(
onTap: () async {
final amount = await Navigator.of(context).pushNamed(Routes.ioniaCustomRedeemPage, arguments: [giftCard]) as String;
if(amount.isNotEmpty){
Navigator.pop(context, amount);
}
},
child: _GradiantContainer(
content: Padding(
padding: const EdgeInsets.only(top: 24, left: 20, right: 24, bottom: 50),
child: Text(
S.of(context).custom_redeem_amount,
style: textXLargeSemiBold(),
),
),
),
)
],
),
children: [
SizedBox(
height: 10,
),
Center(
child: Text(
S.of(context).choose_from_available_options,
style: textMedium(
color: Theme.of(context).primaryTextTheme.headline6!.color!,
),
),
),
SizedBox(height: 40),
InkWell(
onTap: () async {
final amount = await Navigator.of(context)
.pushNamed(Routes.ioniaCustomRedeemPage, arguments: [giftCard]) as String?;
if (amount != null && amount.isNotEmpty) {
Navigator.pop(context);
}
},
child: _GradiantContainer(
content: Padding(
padding: const EdgeInsets.only(top: 24, left: 20, right: 24, bottom: 50),
child: Text(
S.of(context).custom_redeem_amount,
style: textXLargeSemiBold(),
),
),
),
)
],
),
);
}
}
class _GradiantContainer extends StatelessWidget {
const _GradiantContainer({
Key? key,
required this.content
}) : super(key: key);
const _GradiantContainer({Key? key, required this.content}) : super(key: key);
final Widget content;
@@ -20,7 +20,7 @@ class IoniaFilterModal extends StatelessWidget {
padding: EdgeInsets.all(10),
child: Image.asset(
'assets/images/mini_search_icon.png',
color: Theme.of(context).accentColor,
color: Theme.of(context).textTheme.subtitle2!.color!,
),
);
return Scaffold(
@@ -53,7 +53,7 @@ class IoniaFilterModal extends StatelessWidget {
prefixIcon: searchIcon,
hintText: S.of(context).search_category,
contentPadding: EdgeInsets.only(bottom: 5),
fillColor: Theme.of(context).textTheme!.subtitle1!.backgroundColor!,
fillColor: Theme.of(context).primaryTextTheme!.caption!.decorationColor!.withOpacity(0.5),
border: OutlineInputBorder(
borderSide: BorderSide.none,
borderRadius: BorderRadius.circular(8),
@@ -1,4 +1,5 @@
import 'dart:ui';
import 'package:cake_wallet/src/widgets/section_divider.dart';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
@@ -84,10 +85,7 @@ class MoneroAccountListPage extends StatelessWidget {
padding: EdgeInsets.zero,
controller: controller,
separatorBuilder: (context, index) =>
Container(
height: 1,
color: Theme.of(context).dividerColor,
),
const SectionDivider(),
itemCount: accounts.length ?? 0,
itemBuilder: (context, index) {
final account = accounts[index];
@@ -0,0 +1,111 @@
import 'package:cake_wallet/src/screens/nodes/widgets/node_form.dart';
import 'package:cake_wallet/src/screens/settings/widgets/settings_switcher_cell.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:flutter_mobx/flutter_mobx.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/widgets/primary_button.dart';
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
class AdvancedPrivacySettingsPage extends BasePage {
AdvancedPrivacySettingsPage(this.advancedPrivacySettingsViewModel, this.nodeViewModel);
final AdvancedPrivacySettingsViewModel advancedPrivacySettingsViewModel;
final NodeCreateOrEditViewModel nodeViewModel;
@override
String get title => S.current.privacy_settings;
@override
Widget body(BuildContext context) =>
AdvancedPrivacySettingsBody(advancedPrivacySettingsViewModel, nodeViewModel);
}
class AdvancedPrivacySettingsBody extends StatefulWidget {
const AdvancedPrivacySettingsBody(this.privacySettingsViewModel, this.nodeViewModel, {Key? key})
: super(key: key);
final AdvancedPrivacySettingsViewModel privacySettingsViewModel;
final NodeCreateOrEditViewModel nodeViewModel;
@override
_AdvancedPrivacySettingsBodyState createState() => _AdvancedPrivacySettingsBodyState();
}
class _AdvancedPrivacySettingsBodyState extends State<AdvancedPrivacySettingsBody> {
_AdvancedPrivacySettingsBodyState();
final _formKey = GlobalKey<FormState>();
@override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.only(top: 24),
child: ScrollableWithBottomSection(
contentPadding: EdgeInsets.only(bottom: 24),
content: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
...widget.privacySettingsViewModel.settings.map(
(item) => Observer(
builder: (_) => SettingsSwitcherCell(
title: item.title,
value: item.value(),
onValueChange: item.onValueChange,
),
),
),
Observer(
builder: (_) {
if (widget.privacySettingsViewModel.addCustomNode) {
return Padding(
padding: EdgeInsets.only(left: 24, right: 24, top: 24),
child: NodeForm(
formKey: _formKey,
nodeViewModel: widget.nodeViewModel,
),
);
}
return const SizedBox();
},
),
],
),
bottomSectionPadding: EdgeInsets.all(24),
bottomSection: Column(
children: [
LoadingPrimaryButton(
onPressed: () {
if (widget.privacySettingsViewModel.addCustomNode) {
if (_formKey.currentState != null && !_formKey.currentState!.validate()) {
return;
}
widget.nodeViewModel.save(saveAsCurrent: true);
}
Navigator.pop(context);
},
text: S.of(context).continue_text,
color: Theme.of(context).accentTextTheme.bodyText1!.color!,
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).accentTextTheme.headline2?.color,
),
),
),
],
),
),
);
}
}
+24 -12
View File
@@ -200,18 +200,30 @@ class _WalletNameFormState extends State<WalletNameForm> {
]
]),
bottomSectionPadding:
EdgeInsets.only(left: 24, right: 24, bottom: 24),
bottomSection: Observer(
builder: (context) {
return LoadingPrimaryButton(
onPressed: _confirmForm,
text: S.of(context).seed_language_next,
color: Colors.green,
textColor: Colors.white,
isLoading: _walletNewVM.state is IsExecutingState,
isDisabled: _walletNewVM.name.isEmpty,
);
},
EdgeInsets.all(24),
bottomSection: Column(
children: [
Observer(
builder: (context) {
return LoadingPrimaryButton(
onPressed: _confirmForm,
text: S.of(context).seed_language_next,
color: Colors.green,
textColor: Colors.white,
isLoading: _walletNewVM.state is IsExecutingState,
isDisabled: _walletNewVM.name.isEmpty,
);
},
),
const SizedBox(height: 25),
GestureDetector(
onTap: () {
Navigator.of(context)
.pushNamed(Routes.advancedPrivacySettings, arguments: _walletNewVM.type);
},
child: Text(S.of(context).advanced_privacy_settings),
),
],
)),
);
}
@@ -1,16 +1,13 @@
import 'package:cake_wallet/core/execution_state.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/standard_checkbox.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:mobx/mobx.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/core/node_address_validator.dart';
import 'package:cake_wallet/core/node_port_validator.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:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
import 'package:cake_wallet/view_model/node_list/node_create_or_edit_view_model.dart';
@@ -108,76 +105,10 @@ class NodeCreateOrEditPage extends BasePage {
padding: EdgeInsets.only(left: 24, right: 24),
child: ScrollableWithBottomSection(
contentPadding: EdgeInsets.only(bottom: 24.0),
content: Form(
key: _formKey,
child: Column(
children: <Widget>[
Row(
children: <Widget>[
Expanded(
child: BaseTextFormField(
controller: _addressController,
hintText: S.of(context).node_address,
validator: NodeAddressValidator(),
)
)
],
),
SizedBox(height: 10.0),
Row(
children: <Widget>[
Expanded(
child: BaseTextFormField(
controller: _portController,
hintText: S.of(context).node_port,
keyboardType: TextInputType.numberWithOptions(
signed: false, decimal: false),
validator: NodePortValidator(),
)
)
],
),
SizedBox(height: 10.0),
if (nodeCreateOrEditViewModel.hasAuthCredentials) ...[
Row(
children: <Widget>[
Expanded(
child: BaseTextFormField(
controller: _loginController,
hintText: S.of(context).login,
)
)
],
),
SizedBox(height: 10.0),
Row(
children: <Widget>[
Expanded(
child: BaseTextFormField(
controller: _passwordController,
hintText: S.of(context).password,
)
)
],
),
Padding(
padding: EdgeInsets.only(top: 20),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
children: [
Observer(
builder: (_) => StandardCheckbox(
value: nodeCreateOrEditViewModel.useSSL,
onChanged: (value) =>
nodeCreateOrEditViewModel.useSSL = value,
caption: S.of(context).use_ssl,
))
],
))
]
],
)),
content: NodeForm(
formKey: _formKey,
nodeViewModel: nodeCreateOrEditViewModel,
),
bottomSectionPadding: EdgeInsets.only(bottom: 24),
bottomSection: Observer(
builder: (_) => Row(
-166
View File
@@ -1,166 +0,0 @@
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cw_core/node.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/palette.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
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 NodeListPage extends BasePage {
NodeListPage(this.nodeListViewModel);
@override
String get title => S.current.nodes;
final NodeListViewModel nodeListViewModel;
@override
Widget trailing(context) {
return Container(
height: 32,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(16)),
color: Theme.of(context).accentTextTheme.caption!.color!),
child: ButtonTheme(
minWidth: double.minPositive,
child: TextButton(
onPressed: () async {
await showPopUp<void>(
context: context,
builder: (BuildContext context) {
return AlertWithTwoActions(
alertTitle: S.of(context).node_reset_settings_title,
alertContent:
S.of(context).nodes_list_reset_to_default_message,
rightButtonText: S.of(context).reset,
leftButtonText: S.of(context).cancel,
actionRightButton: () async {
Navigator.of(context).pop();
await nodeListViewModel.reset();
},
actionLeftButton: () => Navigator.of(context).pop());
});
},
child: Text(
S.of(context).reset,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 14.0,
fontWeight: FontWeight.w600,
color: Palette.blueCraiola),
)),
),
);
}
@override
Widget body(BuildContext context) {
return Container(
padding: EdgeInsets.only(top: 10),
child: Observer(
builder: (BuildContext context) {
return SectionStandardList(
sectionCount: 2,
context: context,
itemCounter: (int sectionIndex) {
if (sectionIndex == 0) {
return 1;
}
return nodeListViewModel.nodes.length;
},
itemBuilder: (_, sectionIndex, index) {
if (sectionIndex == 0) {
return NodeHeaderListRow(
title: S.of(context).add_new_node,
onTap: (_) async => await Navigator.of(context)
.pushNamed(Routes.newNode));
}
final node = nodeListViewModel.nodes[index];
final isSelected =
node.keyIndex == nodeListViewModel.currentNode.keyIndex;
final nodeListRow = NodeListRow(
title: node.uriRaw,
isSelected: isSelected,
isAlive: node.requestNode(),
onTap: (_) async {
if (isSelected) {
return;
}
await showPopUp<void>(
context: context,
builder: (BuildContext context) {
return AlertWithTwoActions(
alertTitle:
S.of(context).change_current_node_title,
alertContent: S
.of(context)
.change_current_node(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),
endActionPane: _actionPane(context, node),
child: nodeListRow,
);
return isSelected ? nodeListRow : dismissibleRow;
});
},
),
);
}
ActionPane _actionPane(BuildContext context, Node node) => ActionPane(
motion: const ScrollMotion(),
extentRatio: 0.3,
children: [
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,
),
],
);
}
@@ -0,0 +1,151 @@
import 'package:cake_wallet/core/node_address_validator.dart';
import 'package:cake_wallet/core/node_port_validator.dart';
import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
import 'package:cake_wallet/src/widgets/standard_checkbox.dart';
import 'package:cake_wallet/view_model/node_list/node_create_or_edit_view_model.dart';
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:mobx/mobx.dart';
class NodeForm extends StatelessWidget {
NodeForm({
required this.nodeViewModel,
required this.formKey,
}) : _addressController = TextEditingController(),
_portController = TextEditingController(),
_loginController = TextEditingController(),
_passwordController = TextEditingController() {
reaction((_) => nodeViewModel.address, (String address) {
if (address != _addressController.text) {
_addressController.text = address;
}
});
reaction((_) => nodeViewModel.port, (String port) {
if (port != _portController.text) {
_portController.text = port;
}
});
if (nodeViewModel.hasAuthCredentials) {
reaction((_) => nodeViewModel.login, (String login) {
if (login != _loginController.text) {
_loginController.text = login;
}
});
reaction((_) => nodeViewModel.password, (String password) {
if (password != _passwordController.text) {
_passwordController.text = password;
}
});
}
_addressController
.addListener(() => nodeViewModel.address = _addressController.text);
_portController
.addListener(() => nodeViewModel.port = _portController.text);
_loginController
.addListener(() => nodeViewModel.login = _loginController.text);
_passwordController
.addListener(() => nodeViewModel.password = _passwordController.text);
}
final NodeCreateOrEditViewModel nodeViewModel;
final GlobalKey<FormState> formKey;
final TextEditingController _addressController;
final TextEditingController _portController;
final TextEditingController _loginController;
final TextEditingController _passwordController;
@override
Widget build(BuildContext context) {
return Form(
key: formKey,
child: Column(
children: <Widget>[
Row(
children: <Widget>[
Expanded(
child: BaseTextFormField(
controller: _addressController,
hintText: S.of(context).node_address,
validator: NodeAddressValidator(),
),
)
],
),
SizedBox(height: 10.0),
Row(
children: <Widget>[
Expanded(
child: BaseTextFormField(
controller: _portController,
hintText: S.of(context).node_port,
keyboardType: TextInputType.numberWithOptions(
signed: false, decimal: false),
validator: NodePortValidator(),
))
],
),
SizedBox(height: 10.0),
if (nodeViewModel.hasAuthCredentials) ...[
Row(
children: <Widget>[
Expanded(
child: BaseTextFormField(
controller: _loginController,
hintText: S.of(context).login,
))
],
),
SizedBox(height: 10.0),
Row(
children: <Widget>[
Expanded(
child: BaseTextFormField(
controller: _passwordController,
hintText: S.of(context).password,
))
],
),
Padding(
padding: EdgeInsets.only(top: 20),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
children: [
Observer(
builder: (_) => StandardCheckbox(
value: nodeViewModel.useSSL,
onChanged: (value) => nodeViewModel.useSSL = value,
caption: S.of(context).use_ssl,
),
)
],
),
),
Padding(
padding: EdgeInsets.only(top: 20),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
children: [
Observer(
builder: (_) => StandardCheckbox(
value: nodeViewModel.trusted,
onChanged: (value) => nodeViewModel.trusted = value,
caption: S.of(context).trusted,
),
),
],
),
),
]
],
),
);
}
}
@@ -37,7 +37,10 @@ class NodeHeaderListRow extends StandardListRow {
@override
Widget buildTrailing(BuildContext context) {
return Icon(Icons.add,
color: Theme.of(context).accentTextTheme!.subtitle1!.color!, size: 24.0);
return SizedBox(
width: 20,
child: Icon(Icons.add,
color: Theme.of(context).accentTextTheme!.subtitle1!.color!, size: 24.0),
);
}
}
@@ -7,7 +7,7 @@ import 'package:flutter/services.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/widgets/standart_list_row.dart';
import 'package:cake_wallet/src/widgets/list_row.dart';
import 'package:cake_wallet/src/screens/trade_details/track_trade_list_item.dart';
class OrderDetailsPage extends BasePage {
@@ -57,7 +57,7 @@ class OrderDetailsPageBodyState extends State<OrderDetailsPageBody> {
if (item is TrackTradeListItem) {
return GestureDetector(
onTap: item.onTap,
child: StandartListRow(
child: ListRow(
title: '${item.title}', value: '${item.value}'));
} else {
return GestureDetector(
@@ -65,7 +65,7 @@ class OrderDetailsPageBodyState extends State<OrderDetailsPageBody> {
Clipboard.setData(ClipboardData(text: '${item.value}'));
showBar<void>(context, S.of(context).copied_to_clipboard);
},
child: StandartListRow(
child: ListRow(
title: '${item.title}', value: '${item.value}'));
}
});
+2 -2
View File
@@ -1,4 +1,5 @@
import 'package:cake_wallet/src/widgets/keyboard_done_button.dart';
import 'package:cake_wallet/src/widgets/section_divider.dart';
import 'package:cake_wallet/themes/theme_base.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
@@ -135,8 +136,7 @@ class ReceivePage extends BasePage {
Observer(
builder: (_) => ListView.separated(
padding: EdgeInsets.all(0),
separatorBuilder: (context, _) => Container(
height: 1, color: Theme.of(context).dividerColor),
separatorBuilder: (context, _) => const SectionDivider(),
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: addressListViewModel.items.length,
+67 -14
View File
@@ -1,23 +1,28 @@
import 'dart:async';
import 'package:cake_wallet/core/auth_service.dart';
import 'package:cake_wallet/utils/payment_request.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/auth/auth_page.dart';
import 'package:cake_wallet/store/app_store.dart';
import 'package:cake_wallet/store/authentication_store.dart';
import 'package:cake_wallet/entities/qr_scanner.dart';
import 'package:uni_links/uni_links.dart';
class Root extends StatefulWidget {
Root(
{required Key key,
required this.authenticationStore,
required this.appStore,
required this.child,
required this.navigatorKey})
: super(key: key);
Root({
required Key key,
required this.authenticationStore,
required this.appStore,
required this.child,
required this.navigatorKey,
required this.authService,
}) : super(key: key);
final AuthenticationStore authenticationStore;
final AppStore appStore;
final GlobalKey<NavigatorState> navigatorKey;
final AuthService authService;
final Widget child;
@override
@@ -26,22 +31,56 @@ class Root extends StatefulWidget {
class RootState extends State<Root> with WidgetsBindingObserver {
RootState()
: _isInactiveController = StreamController<bool>.broadcast(),
_isInactive = false,
_postFrameCallback = false;
: _isInactiveController = StreamController<bool>.broadcast(),
_isInactive = false,
_requestAuth = true,
_postFrameCallback = false;
Stream<bool> get isInactive => _isInactiveController.stream;
StreamController<bool> _isInactiveController;
bool _isInactive;
bool _postFrameCallback;
bool _requestAuth;
StreamSubscription<Uri?>? stream;
Uri? launchUri;
@override
void initState() {
_requestAuth = widget.authService.requireAuth();
_isInactiveController = StreamController<bool>.broadcast();
_isInactive = false;
_postFrameCallback = false;
WidgetsBinding.instance.addObserver(this);
super.initState();
initUniLinks();
}
@override
void dispose() {
stream?.cancel();
super.dispose();
}
/// handle app links while the app is already started
/// whether its in the foreground or in the background.
Future<void> initUniLinks() async {
try {
stream = uriLinkStream.listen((Uri? uri) {
handleDeepLinking(uri);
});
handleDeepLinking(await getInitialUri());
} catch (e) {
print(e);
}
}
void handleDeepLinking(Uri? uri) {
if (uri == null || !mounted) return;
launchUri = uri;
}
@override
@@ -52,11 +91,15 @@ class RootState extends State<Root> with WidgetsBindingObserver {
return;
}
if (!_isInactive &&
widget.authenticationStore.state == AuthenticationState.allowed) {
if (!_isInactive && widget.authenticationStore.state == AuthenticationState.allowed) {
setState(() => _setInactive(true));
}
break;
case AppLifecycleState.resumed:
setState(() {
_requestAuth = widget.authService.requireAuth();
});
break;
default:
break;
@@ -65,7 +108,7 @@ class RootState extends State<Root> with WidgetsBindingObserver {
@override
Widget build(BuildContext context) {
if (_isInactive && !_postFrameCallback) {
if (_isInactive && !_postFrameCallback && _requestAuth) {
_postFrameCallback = true;
WidgetsBinding.instance.addPostFrameCallback((_) {
widget.navigatorKey.currentState?.pushNamed(Routes.unlock,
@@ -75,9 +118,19 @@ class RootState extends State<Root> with WidgetsBindingObserver {
}
_reset();
auth.close();
auth.close(
route: launchUri != null ? Routes.send : null,
arguments: PaymentRequest.fromUri(launchUri),
);
launchUri = null;
});
});
} else if (launchUri != null) {
widget.navigatorKey.currentState?.pushNamed(
Routes.send,
arguments: PaymentRequest.fromUri(launchUri),
);
launchUri = null;
}
return WillPopScope(onWillPop: () async => false, child: widget.child);
+11 -16
View File
@@ -1,13 +1,11 @@
import 'dart:ui';
import 'package:cake_wallet/entities/fiat_currency.dart';
import 'package:cake_wallet/src/screens/dashboard/widgets/sync_indicator_icon.dart';
import 'package:cake_wallet/src/screens/send/widgets/send_card.dart';
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
import 'package:cake_wallet/src/widgets/picker.dart';
import 'package:cake_wallet/src/widgets/template_tile.dart';
import 'package:cake_wallet/utils/payment_request.dart';
import 'package:cake_wallet/view_model/send/output.dart';
import 'package:cake_wallet/view_model/settings/settings_view_model.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:mobx/mobx.dart';
@@ -28,13 +26,15 @@ import 'package:smooth_page_indicator/smooth_page_indicator.dart';
import 'package:cw_core/crypto_currency.dart';
class SendPage extends BasePage {
SendPage({required this.sendViewModel,required this.settingsViewModel }) : _formKey = GlobalKey<FormState>(),fiatFromSettings = settingsViewModel.fiatCurrency;
SendPage({
required this.sendViewModel,
this.initialPaymentRequest,
}) : _formKey = GlobalKey<FormState>();
final SendViewModel sendViewModel;
final SettingsViewModel settingsViewModel;
final GlobalKey<FormState> _formKey;
final controller = PageController(initialPage: 0);
final FiatCurrency fiatFromSettings ;
final PaymentRequest? initialPaymentRequest;
bool _effectsInstalled = false;
@@ -55,7 +55,7 @@ class SendPage extends BasePage {
@override
void onClose(BuildContext context) {
settingsViewModel.setFiatCurrency(fiatFromSettings);
sendViewModel.onClose();
Navigator.of(context).pop();
}
@@ -121,6 +121,7 @@ class SendPage extends BasePage {
key: output.key,
output: output,
sendViewModel: sendViewModel,
initialPaymentRequest: initialPaymentRequest,
);
});
},
@@ -236,7 +237,7 @@ class SendPage extends BasePage {
if(template.isCurrencySelected){
output.setCryptoAmount(template.amount);
}else{
settingsViewModel.setFiatCurrency(fiatFromTemplate);
sendViewModel.setFiatCurrency(fiatFromTemplate);
output.setFiatAmount(template.amountFiat);
}
output.resetParsedAddress();
@@ -386,16 +387,10 @@ class SendPage extends BasePage {
amount: S.of(context).send_amount,
amountValue:
sendViewModel.pendingTransaction!.amountFormatted,
fiatAmountValue:
sendViewModel.pendingTransactionFiatAmount +
' ' +
sendViewModel.fiat.title,
fiatAmountValue: sendViewModel.pendingTransactionFiatAmountFormatted,
fee: S.of(context).send_fee,
feeValue: sendViewModel.pendingTransaction!.feeFormatted,
feeFiatAmount:
sendViewModel.pendingTransactionFeeFiatAmount +
' ' +
sendViewModel.fiat.title,
feeFiatAmount: sendViewModel.pendingTransactionFeeFiatAmountFormatted,
outputs: sendViewModel.outputs,
rightButtonText: S.of(context).ok,
leftButtonText: S.of(context).cancel,
@@ -1,4 +1,5 @@
import 'package:cake_wallet/src/widgets/cake_scrollbar.dart';
import 'package:cake_wallet/src/widgets/section_divider.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/src/widgets/base_alert_dialog.dart';
@@ -70,10 +71,7 @@ class ChooseYatAddressButtonsState extends State<ChooseYatAddressButtons> {
controller: controller,
padding: EdgeInsets.all(0),
itemCount: itemCount,
separatorBuilder: (_, __) => Container(
height: 1,
color: Theme.of(context).dividerColor,
),
separatorBuilder: (_, __) => const SectionDivider(),
itemBuilder: (context, index) {
final address = addresses[index];
+53 -10
View File
@@ -1,11 +1,11 @@
import 'dart:ui';
import 'package:cake_wallet/entities/priority_for_wallet_type.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cake_wallet/utils/payment_request.dart';
import 'package:cw_core/transaction_priority.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/widgets/keyboard_done_button.dart';
import 'package:cake_wallet/src/widgets/picker.dart';
import 'package:cake_wallet/view_model/send/output.dart';
import 'package:cake_wallet/view_model/settings/settings_view_model.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
@@ -21,21 +21,28 @@ class SendCard extends StatefulWidget {
SendCard({
Key? key,
required this.output,
required this.sendViewModel}) : super(key: key);
required this.sendViewModel,
this.initialPaymentRequest,
}) : super(key: key);
final Output output;
final SendViewModel sendViewModel;
final PaymentRequest? initialPaymentRequest;
@override
SendCardState createState() => SendCardState(
output: output,
sendViewModel: sendViewModel
sendViewModel: sendViewModel,
initialPaymentRequest: initialPaymentRequest,
);
}
class SendCardState extends State<SendCard>
with AutomaticKeepAliveClientMixin<SendCard> {
SendCardState({required this.output, required this.sendViewModel})
SendCardState({
required this.output,
required this.sendViewModel,
this.initialPaymentRequest})
: addressController = TextEditingController(),
cryptoAmountController = TextEditingController(),
fiatAmountController = TextEditingController(),
@@ -50,6 +57,7 @@ class SendCardState extends State<SendCard>
final Output output;
final SendViewModel sendViewModel;
final PaymentRequest? initialPaymentRequest;
final TextEditingController addressController;
final TextEditingController cryptoAmountController;
@@ -62,6 +70,27 @@ class SendCardState extends State<SendCard>
bool _effectsInstalled = false;
@override
void initState() {
super.initState();
/// if the current wallet doesn't match the one in the qr code
if (initialPaymentRequest != null &&
sendViewModel.walletCurrencyName != initialPaymentRequest!.scheme.toLowerCase()) {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
showPopUp<void>(
context: context,
builder: (BuildContext context) {
return AlertWithOneAction(
alertTitle: S.of(context).error,
alertContent: S.of(context).unmatched_currencies,
buttonText: S.of(context).ok,
buttonAction: () => Navigator.of(context).pop());
});
});
}
}
@override
Widget build(BuildContext context) {
super.build(context);
@@ -155,6 +184,7 @@ class SendCardState extends State<SendCard>
await output.fetchParsedAddress(context);
},
validator: validator,
selectedCurrency: sendViewModel.currency,
);
}),
if (output.isParsedAddress) Padding(
@@ -332,7 +362,8 @@ class SendCardState extends State<SendCard>
],
),
)),
Padding(
if (!sendViewModel.isFiatDisabled)
Padding(
padding: const EdgeInsets.only(top: 20),
child: BaseTextFormField(
focusNode: fiatAmountFocus,
@@ -438,8 +469,9 @@ class SendCardState extends State<SendCard>
Padding(
padding:
EdgeInsets.only(top: 5),
child: Text(
output
child: sendViewModel.isFiatDisabled
? const SizedBox(height: 14)
: Text(output
.estimatedFeeFiatAmount
+ ' ' +
sendViewModel
@@ -510,8 +542,12 @@ class SendCardState extends State<SendCard>
}
void _setEffects(BuildContext context) {
addressController.text = output.address;
cryptoAmountController.text = output.cryptoAmount;
if (output.address.isNotEmpty) {
addressController.text = output.address;
}
if (output.cryptoAmount.isNotEmpty) {
cryptoAmountController.text = output.cryptoAmount;
}
fiatAmountController.text = output.fiatAmount;
noteController.text = output.note;
extractedAddressController.text = output.extractedAddress;
@@ -603,6 +639,13 @@ class SendCardState extends State<SendCard>
extractedAddressController.text = extractedAddress;
});
if (initialPaymentRequest != null &&
sendViewModel.walletCurrencyName == initialPaymentRequest!.scheme.toLowerCase()) {
addressController.text = initialPaymentRequest!.address;
cryptoAmountController.text = initialPaymentRequest!.amount;
noteController.text = initialPaymentRequest!.note;
}
_effectsInstalled = true;
}
@@ -0,0 +1,155 @@
import 'package:cake_wallet/src/screens/settings/widgets/settings_cell_with_arrow.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
import 'package:cw_core/node.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';
import 'package:cake_wallet/src/screens/base_page.dart';
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);
@override
String get title => S.current.connection_sync;
final NodeListViewModel nodeListViewModel;
final DashboardViewModel dashboardViewModel;
@override
Widget body(BuildContext context) {
return Container(
padding: EdgeInsets.only(top: 10),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
SettingsCellWithArrow(
title: S.current.reconnect,
handler: (context) => _presentReconnectAlert(context),
),
StandardListSeparator(padding: EdgeInsets.symmetric(horizontal: 24)),
SettingsCellWithArrow(
title: S.current.rescan,
handler: (context) => Navigator.of(context).pushNamed(Routes.rescan),
),
StandardListSeparator(padding: EdgeInsets.symmetric(horizontal: 24)),
NodeHeaderListRow(
title: S.of(context).add_new_node,
onTap: (_) async => await Navigator.of(context).pushNamed(Routes.newNode),
),
StandardListSeparator(padding: EdgeInsets.symmetric(horizontal: 24)),
SizedBox(height: 100),
Observer(
builder: (BuildContext context) {
return Flexible(
child: SectionStandardList(
sectionCount: 1,
context: context,
dividerPadding: EdgeInsets.symmetric(horizontal: 24),
itemCounter: (int sectionIndex) {
return nodeListViewModel.nodes.length;
},
itemBuilder: (_, sectionIndex, index) {
final node = nodeListViewModel.nodes[index];
final isSelected = node.keyIndex == nodeListViewModel.currentNode.keyIndex;
final nodeListRow = NodeListRow(
title: node.uriRaw,
isSelected: isSelected,
isAlive: node.requestNode(),
onTap: (_) async {
if (isSelected) {
return;
}
await showPopUp<void>(
context: context,
builder: (BuildContext context) {
return AlertWithTwoActions(
alertTitle: S.of(context).change_current_node_title,
alertContent: S.of(context).change_current_node(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),
endActionPane: _actionPane(context, node),
child: nodeListRow,
);
return isSelected ? nodeListRow : dismissibleRow;
},
),
);
},
),
],
),
);
}
Future<void> _presentReconnectAlert(BuildContext context) async {
await showPopUp<void>(
context: context,
builder: (BuildContext context) {
return AlertWithTwoActions(
alertTitle: S.of(context).reconnection,
alertContent: S.of(context).reconnect_alert_text,
rightButtonText: S.of(context).ok,
leftButtonText: S.of(context).cancel,
actionRightButton: () async {
Navigator.of(context).pop();
await dashboardViewModel.reconnect();
},
actionLeftButton: () => Navigator.of(context).pop());
},
);
}
ActionPane _actionPane(BuildContext context, Node node) => ActionPane(
motion: const ScrollMotion(),
extentRatio: 0.3,
children: [
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,
),
],
);
}
@@ -0,0 +1,81 @@
import 'package:cake_wallet/entities/fiat_currency.dart';
import 'package:cake_wallet/entities/language_service.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/screens/settings/widgets/settings_choices_cell.dart';
import 'package:cake_wallet/src/screens/settings/widgets/settings_picker_cell.dart';
import 'package:cake_wallet/src/screens/settings/widgets/settings_switcher_cell.dart';
import 'package:cake_wallet/themes/theme_base.dart';
import 'package:cake_wallet/themes/theme_list.dart';
import 'package:cake_wallet/view_model/settings/choices_list_item.dart';
import 'package:cake_wallet/view_model/settings/display_settings_view_model.dart';
import 'package:cake_wallet/wallet_type_utils.dart';
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
class DisplaySettingsPage extends BasePage {
DisplaySettingsPage(this._displaySettingsViewModel);
@override
String get title => S.current.display_settings;
final DisplaySettingsViewModel _displaySettingsViewModel;
@override
Widget body(BuildContext context) {
return Observer(builder: (_) {
return Container(
padding: EdgeInsets.only(top: 10),
child: Column(
children: [
SettingsSwitcherCell(
title: S.current.settings_display_balance,
value: _displaySettingsViewModel.shouldDisplayBalance,
onValueChange: (_, bool value) {
_displaySettingsViewModel.setShouldDisplayBalance(value);
}),
//if (!isHaven) it does not work correctly
if(!_displaySettingsViewModel.disabledFiatApiMode)
SettingsPickerCell<FiatCurrency>(
title: S.current.settings_currency,
searchHintText: S.current.search_currency,
items: FiatCurrency.all,
selectedItem: _displaySettingsViewModel.fiatCurrency,
onItemSelected: (FiatCurrency currency) => _displaySettingsViewModel.setFiatCurrency(currency),
images: FiatCurrency.all.map((e) => Image.asset("assets/images/flags/${e.countryCode}.png")).toList(),
isGridView: true,
matchingCriteria: (FiatCurrency currency, String searchText) {
return currency.title.toLowerCase().contains(searchText) ||
currency.fullName.toLowerCase().contains(searchText);
},
),
SettingsPickerCell<String>(
title: S.current.settings_change_language,
searchHintText: S.current.search_language,
items: LanguageService.list.keys.toList(),
displayItem: (dynamic code) {
return LanguageService.list[code] ?? '';
},
selectedItem: _displaySettingsViewModel.languageCode,
onItemSelected: _displaySettingsViewModel.onLanguageSelected,
images: LanguageService.list.keys
.map((e) => Image.asset("assets/images/flags/${LanguageService.localeCountryCode[e]}.png"))
.toList(),
matchingCriteria: (String code, String searchText) {
return LanguageService.list[code]?.toLowerCase().contains(searchText) ?? false;
},
),
SettingsChoicesCell(
ChoicesListItem<ThemeBase>(
title: S.current.color_theme,
items: ThemeList.all,
selectedItem: _displaySettingsViewModel.theme,
onItemSelected: (ThemeBase theme) => _displaySettingsViewModel.setTheme(theme),
),
),
],
),
);
});
}
}
@@ -0,0 +1,45 @@
import 'package:cake_wallet/entities/priority_for_wallet_type.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/screens/settings/widgets/settings_cell_with_arrow.dart';
import 'package:cake_wallet/src/screens/settings/widgets/settings_picker_cell.dart';
import 'package:cake_wallet/src/screens/settings/widgets/settings_version_cell.dart';
import 'package:cake_wallet/src/widgets/standard_list.dart';
import 'package:cake_wallet/view_model/settings/other_settings_view_model.dart';
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
class OtherSettingsPage extends BasePage {
OtherSettingsPage(this._otherSettingsViewModel);
@override
String get title => S.current.other_settings;
final OtherSettingsViewModel _otherSettingsViewModel;
@override
Widget body(BuildContext context) {
return Observer(builder: (_) {
return Container(
padding: EdgeInsets.only(top: 10),
child: Column(children: [
SettingsPickerCell(
title: S.current.settings_fee_priority,
items: priorityForWalletType(_otherSettingsViewModel.walletType),
displayItem: _otherSettingsViewModel.getDisplayPriority,
selectedItem: _otherSettingsViewModel.transactionPriority,
onItemSelected: _otherSettingsViewModel.onDisplayPrioritySelected,
),
SettingsCellWithArrow(
title: S.current.settings_terms_and_conditions,
handler: (BuildContext context) => Navigator.of(context).pushNamed(Routes.readDisclaimer),
),
StandardListSeparator(padding: EdgeInsets.symmetric(horizontal: 24)),
Spacer(),
SettingsVersionCell(title: S.of(context).version(_otherSettingsViewModel.currentVersion))
]),
);
});
}
}
@@ -0,0 +1,47 @@
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/screens/settings/widgets/settings_switcher_cell.dart';
import 'package:cake_wallet/view_model/settings/privacy_settings_view_model.dart';
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
class PrivacyPage extends BasePage {
PrivacyPage(this._privacySettingsViewModel);
@override
String get title => S.current.privacy_settings;
final PrivacySettingsViewModel _privacySettingsViewModel;
@override
Widget body(BuildContext context) {
return Container(
padding: EdgeInsets.only(top: 10),
child: Observer(builder: (_) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
SettingsSwitcherCell(
title: S.current.disable_fiat,
value: _privacySettingsViewModel.isFiatDisabled,
onValueChange: (BuildContext context, bool value) {
_privacySettingsViewModel.setFiatMode(value);
}),
SettingsSwitcherCell(
title: S.current.disable_exchange,
value: _privacySettingsViewModel.disableExchange,
onValueChange: (BuildContext context, bool value) {
_privacySettingsViewModel.setEnableExchange(value);
}),
SettingsSwitcherCell(
title: S.current.settings_save_recipient_address,
value: _privacySettingsViewModel.shouldSaveRecipientAddress,
onValueChange: (BuildContext _, bool value) {
_privacySettingsViewModel.setShouldSaveRecipientAddress(value);
})
],
);
}),
);
}
}
@@ -0,0 +1,102 @@
import 'package:cake_wallet/entities/pin_code_required_duration.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/auth/auth_page.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/screens/pin_code/pin_code_widget.dart';
import 'package:cake_wallet/src/screens/settings/widgets/settings_cell_with_arrow.dart';
import 'package:cake_wallet/src/screens/settings/widgets/settings_picker_cell.dart';
import 'package:cake_wallet/src/screens/settings/widgets/settings_switcher_cell.dart';
import 'package:cake_wallet/src/widgets/standard_list.dart';
import 'package:cake_wallet/view_model/settings/security_settings_view_model.dart';
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
class SecurityBackupPage extends BasePage {
SecurityBackupPage(this._securitySettingsViewModel);
@override
String get title => S.current.security_and_backup;
final SecuritySettingsViewModel _securitySettingsViewModel;
@override
Widget body(BuildContext context) {
return Container(
padding: EdgeInsets.only(top: 10),
child: Column(mainAxisSize: MainAxisSize.min, children: [
SettingsCellWithArrow(
title: S.current.show_keys,
handler: (_) => _securitySettingsViewModel.checkPinCodeRiquired()
? Navigator.of(context).pushNamed(Routes.auth,
arguments: (bool isAuthenticatedSuccessfully, AuthPageState auth) {
if (isAuthenticatedSuccessfully) {
auth.close(route: Routes.showKeys);
}
})
: Navigator.of(context).pushNamed(Routes.showKeys),
),
StandardListSeparator(padding: EdgeInsets.symmetric(horizontal: 24)),
SettingsCellWithArrow(
title: S.current.create_backup,
handler: (_) => _securitySettingsViewModel.checkPinCodeRiquired()
? Navigator.of(context).pushNamed(Routes.auth,
arguments: (bool isAuthenticatedSuccessfully, AuthPageState auth) {
if (isAuthenticatedSuccessfully) {
auth.close(route: Routes.backup);
}
})
: Navigator.of(context).pushNamed(Routes.backup),
),
StandardListSeparator(padding: EdgeInsets.symmetric(horizontal: 24)),
SettingsCellWithArrow(
title: S.current.settings_change_pin,
handler: (_) => Navigator.of(context).pushNamed(Routes.auth,
arguments: (bool isAuthenticatedSuccessfully, AuthPageState auth) {
auth.close(
route: isAuthenticatedSuccessfully ? Routes.setupPin : null,
arguments: (PinCodeState<PinCodeWidget> setupPinContext, String _) {
setupPinContext.close();
},
);
})),
StandardListSeparator(padding: EdgeInsets.symmetric(horizontal: 24)),
Observer(builder: (_) {
return SettingsSwitcherCell(
title: S.current.settings_allow_biometrical_authentication,
value: _securitySettingsViewModel.allowBiometricalAuthentication,
onValueChange: (BuildContext context, bool value) {
if (value) {
Navigator.of(context).pushNamed(Routes.auth,
arguments: (bool isAuthenticatedSuccessfully, AuthPageState auth) async {
if (isAuthenticatedSuccessfully) {
if (await _securitySettingsViewModel.biometricAuthenticated()) {
_securitySettingsViewModel
.setAllowBiometricalAuthentication(isAuthenticatedSuccessfully);
}
} else {
_securitySettingsViewModel
.setAllowBiometricalAuthentication(isAuthenticatedSuccessfully);
}
auth.close();
});
} else {
_securitySettingsViewModel.setAllowBiometricalAuthentication(value);
}
});
}),
Observer(builder: (_) {
return SettingsPickerCell<PinCodeRequiredDuration>(
title: S.current.require_pin_after,
items: PinCodeRequiredDuration.values,
selectedItem: _securitySettingsViewModel.pinCodeRequiredDuration,
onItemSelected: (PinCodeRequiredDuration code) {
_securitySettingsViewModel.setPinCodeRequiredDuration(code);
},
);
}),
]),
);
}
}
-98
View File
@@ -1,98 +0,0 @@
import 'package:cake_wallet/src/screens/settings/widgets/settings_choices_cell.dart';
import 'package:cake_wallet/src/screens/settings/widgets/settings_version_cell.dart';
import 'package:cake_wallet/view_model/settings/choices_list_item.dart';
import 'package:cake_wallet/view_model/settings/version_list_item.dart';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/view_model/settings/settings_view_model.dart';
import 'package:cake_wallet/view_model/settings/link_list_item.dart';
import 'package:cake_wallet/view_model/settings/picker_list_item.dart';
import 'package:cake_wallet/view_model/settings/regular_list_item.dart';
import 'package:cake_wallet/view_model/settings/switcher_list_item.dart';
import 'package:cake_wallet/src/screens/settings/widgets/settings_link_provider_cell.dart';
import 'package:cake_wallet/src/screens/settings/widgets/settings_cell_with_arrow.dart';
import 'package:cake_wallet/src/screens/settings/widgets/settings_picker_cell.dart';
import 'package:cake_wallet/src/screens/settings/widgets/settings_switcher_cell.dart';
import 'package:cake_wallet/src/widgets/standard_list.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
class SettingsPage extends BasePage {
SettingsPage(this.settingsViewModel);
final SettingsViewModel settingsViewModel;
@override
String get title => S.current.settings_title;
@override
Widget body(BuildContext context) {
// FIX-ME: Added `context` it was not used here before, maby bug ?
return SectionStandardList(
context: context,
sectionCount: settingsViewModel.sections.length,
itemCounter: (int sectionIndex) {
if (sectionIndex < settingsViewModel.sections.length) {
return settingsViewModel.sections[sectionIndex].length;
}
return 0;
},
itemBuilder: (_, sectionIndex, itemIndex) {
final item = settingsViewModel.sections[sectionIndex][itemIndex];
if (item is PickerListItem) {
return Observer(builder: (_) {
return SettingsPickerCell<Object>(
displayItem: item.displayItem,
title: item.title,
selectedItem: item.selectedItem(),
items: item.items,
onItemSelected: (dynamic value) => item.onItemSelected(value),
images: item.images,
searchHintText: item.searchHintText,
isGridView: item.isGridView,
matchingCriteria: (dynamic value, String searchText) => item.matchingCriteria(value, searchText),
);
});
}
if (item is SwitcherListItem) {
return Observer(builder: (_) {
return SettingsSwitcherCell(
title: item.title,
value: item.value(),
onValueChange: item.onValueChange);
});
}
if (item is RegularListItem) {
return SettingsCellWithArrow(
title: item.title, handler: item.handler);
}
if (item is LinkListItem) {
return SettingsLinkProviderCell(
title: item.title,
icon: item.icon,
link: item.link,
linkTitle: item.linkTitle);
}
if (item is VersionListItem) {
return Observer(builder: (_) {
return SettingsVersionCell(
title:
S.of(context).version(settingsViewModel.currentVersion));
});
}
if (item is ChoicesListItem) {
return SettingsChoicesCell(item);
}
return Container();
});
}
}
@@ -3,7 +3,7 @@ import 'package:flutter/material.dart';
import 'package:cake_wallet/src/widgets/picker.dart';
import 'package:cake_wallet/src/widgets/standard_list.dart';
class SettingsPickerCell<ItemType extends Object> extends StandardListRow {
class SettingsPickerCell<ItemType> extends StandardListRow {
SettingsPickerCell(
{required String title,
required this.selectedItem,
@@ -1,6 +1,6 @@
import 'package:flutter/cupertino.dart';
import 'package:cake_wallet/src/widgets/standard_list.dart';
import 'package:cake_wallet/src/widgets/standart_switch.dart';
import 'package:cake_wallet/src/widgets/standard_switch.dart';
class SettingsSwitcherCell extends StandardListRow {
SettingsSwitcherCell(
@@ -11,6 +11,6 @@ class SettingsSwitcherCell extends StandardListRow {
final void Function(BuildContext context, bool value)? onValueChange;
@override
Widget buildTrailing(BuildContext context) => StandartSwitch(
Widget buildTrailing(BuildContext context) => StandardSwitch(
value: value, onTaped: () => onValueChange?.call(context, !value));
}
@@ -1,6 +1,6 @@
import 'package:cake_wallet/src/widgets/standard_list.dart';
import 'package:cake_wallet/src/widgets/standart_list_card.dart';
import 'package:cake_wallet/src/widgets/standart_list_status_row.dart';
import 'package:cake_wallet/src/widgets/standard_list_card.dart';
import 'package:cake_wallet/src/widgets/standard_list_status_row.dart';
import 'package:cake_wallet/utils/show_bar.dart';
import 'package:cake_wallet/view_model/trade_details_view_model.dart';
import 'package:flutter/material.dart';
@@ -9,7 +9,7 @@ import 'package:flutter/services.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/widgets/standart_list_row.dart';
import 'package:cake_wallet/src/widgets/list_row.dart';
import 'package:cake_wallet/src/screens/trade_details/track_trade_list_item.dart';
import 'package:cake_wallet/src/screens/trade_details/trade_details_list_card.dart';
import 'package:cake_wallet/src/screens/trade_details/trade_details_status_item.dart';
@@ -62,18 +62,18 @@ class TradeDetailsPageBodyState extends State<TradeDetailsPageBody> {
if (item is TrackTradeListItem) {
return GestureDetector(
onTap: item.onTap,
child: StandartListRow(
child: ListRow(
title: '${item.title}', value: '${item.value}'));
}
if (item is DetailsListStatusItem) {
return StandartListStatusRow(
return StandardListStatusRow(
title: item.title,
value: item.value);
}
if (item is TradeDetailsListCardItem) {
return TradeDatailsStandartListCard(
return TradeDetailsStandardListCard(
id: item.id,
create: item.createdAt,
pair: item.pair,
@@ -86,7 +86,7 @@ class TradeDetailsPageBodyState extends State<TradeDetailsPageBody> {
Clipboard.setData(ClipboardData(text: '${item.value}'));
showBar<void>(context, S.of(context).copied_to_clipboard);
},
child: StandartListRow(
child: ListRow(
title: '${item.title}', value: '${item.value}'));
});
});
@@ -6,7 +6,7 @@ import 'package:cake_wallet/view_model/transaction_details_view_model.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/widgets/standart_list_row.dart';
import 'package:cake_wallet/src/widgets/list_row.dart';
import 'package:cake_wallet/src/screens/transaction_details/blockexplorer_list_item.dart';
import 'package:cake_wallet/src/screens/transaction_details/standart_list_item.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
@@ -42,7 +42,7 @@ class TransactionDetailsPage extends BasePage {
S.of(context).transaction_details_copied(item.title));
},
child:
StandartListRow(title: '${item.title}:', value: item.value),
ListRow(title: '${item.title}:', value: item.value),
);
}
@@ -50,7 +50,7 @@ class TransactionDetailsPage extends BasePage {
return GestureDetector(
onTap: item.onTap,
child:
StandartListRow(title: '${item.title}:', value: item.value),
ListRow(title: '${item.title}:', value: item.value),
);
}
@@ -5,7 +5,7 @@ import 'package:cake_wallet/src/widgets/standard_list.dart';
import 'package:cake_wallet/view_model/unspent_coins/unspent_coins_details_view_model.dart';
import 'package:cake_wallet/view_model/unspent_coins/unspent_coins_switch_item.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/src/widgets/standart_list_row.dart';
import 'package:cake_wallet/src/widgets/list_row.dart';
import 'package:cake_wallet/src/screens/transaction_details/standart_list_item.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
@@ -30,7 +30,7 @@ class UnspentCoinsDetailsPage extends BasePage {
final item = unspentCoinsDetailsViewModel.items[index];
if (item is StandartListItem) {
return StandartListRow(
return ListRow(
title: '${item.title}:',
value: item.value);
}
@@ -1,4 +1,4 @@
import 'package:cake_wallet/src/widgets/standart_switch.dart';
import 'package:cake_wallet/src/widgets/standard_switch.dart';
import 'package:flutter/material.dart';
class UnspentCoinsSwitchRow extends StatelessWidget {
@@ -33,7 +33,7 @@ class UnspentCoinsSwitchRow extends StatelessWidget {
textAlign: TextAlign.left),
Padding(
padding: EdgeInsets.only(top: 12),
child: StandartSwitch(
child: StandardSwitch(
value: switchValue,
onTaped: () => onSwitchValueChange(!switchValue))
)
@@ -1,4 +1,5 @@
import 'package:auto_size_text/auto_size_text.dart';
import 'package:cake_wallet/src/widgets/section_divider.dart';
import 'package:cake_wallet/utils/show_bar.dart';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
@@ -6,7 +7,7 @@ import 'package:flutter/services.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/widgets/standart_list_row.dart';
import 'package:cake_wallet/src/widgets/list_row.dart';
import 'package:cake_wallet/view_model/wallet_keys_view_model.dart';
class WalletKeysPage extends BasePage {
@@ -57,10 +58,7 @@ class WalletKeysPage extends BasePage {
height: 1,
padding: EdgeInsets.only(left: 24),
color: Theme.of(context).accentTextTheme!.headline6!.backgroundColor!,
child: Container(
height: 1,
color: Theme.of(context).dividerColor,
),
child: const SectionDivider(),
),
itemCount: walletKeysViewModel.items.length,
itemBuilder: (BuildContext context, int index) {
@@ -71,7 +69,7 @@ class WalletKeysPage extends BasePage {
Clipboard.setData(ClipboardData(text: item.value));
showBar<void>(context, S.of(context).copied_key_to_clipboard(item.title));
},
child: StandartListRow(
child: ListRow(
title: item.title + ':',
value: item.value,
),
@@ -220,68 +220,88 @@ class WalletListBodyState extends State<WalletListBody> {
}
Future<void> _loadWallet(WalletListItem wallet) async {
await Navigator.of(context).pushNamed(Routes.auth, arguments:
(bool isAuthenticatedSuccessfully, AuthPageState auth) async {
if (!isAuthenticatedSuccessfully) {
return;
}
if (await widget.walletListViewModel.checkIfAuthRequired()) {
await Navigator.of(context).pushNamed(Routes.auth,
arguments: (bool isAuthenticatedSuccessfully, AuthPageState auth) async {
if (!isAuthenticatedSuccessfully) {
return;
}
try {
auth.changeProcessText(S.of(context).wallet_list_loading_wallet(wallet.name));
await widget.walletListViewModel.loadWallet(wallet);
auth.hideProgressText();
auth.close();
WidgetsBinding.instance.addPostFrameCallback((_) {
Navigator.of(context).pop();
});
} catch (e) {
auth.changeProcessText(
S.of(context).wallet_list_failed_to_load(wallet.name, e.toString()));
}
});
} else {
try {
auth.changeProcessText(
S.of(context).wallet_list_loading_wallet(wallet.name));
changeProcessText(S.of(context).wallet_list_loading_wallet(wallet.name));
await widget.walletListViewModel.loadWallet(wallet);
auth.hideProgressText();
auth.close();
WidgetsBinding.instance.addPostFrameCallback((_) {
Navigator.of(context).pop();
});
hideProgressText();
Navigator.of(context).pop();
} catch (e) {
auth.changeProcessText(S
.of(context)
.wallet_list_failed_to_load(wallet.name, e.toString()));
changeProcessText(S.of(context).wallet_list_failed_to_load(wallet.name, e.toString()));
}
});
}
}
Future<void> _removeWallet(WalletListItem wallet) async {
await Navigator.of(context).pushNamed(Routes.auth, arguments:
(bool isAuthenticatedSuccessfully, AuthPageState auth) async {
if (!isAuthenticatedSuccessfully) {
return;
}
bool confirmed = false;
await showPopUp<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 {
auth.changeProcessText(
S.of(context).wallet_list_removing_wallet(wallet.name));
await widget.walletListViewModel.remove(wallet);
} catch (e) {
auth.changeProcessText(S
.of(context)
.wallet_list_failed_to_remove(wallet.name, e.toString()));
if (widget.walletListViewModel.checkIfAuthRequired()) {
await Navigator.of(context).pushNamed(Routes.auth,
arguments: (bool isAuthenticatedSuccessfully, AuthPageState auth) async {
if (!isAuthenticatedSuccessfully) {
return;
}
}
_onSuccessfulAuth(wallet, auth);
});
} else {
_onSuccessfulAuth(wallet, null);
}
}
auth.close();
});
void _onSuccessfulAuth(WalletListItem wallet, AuthPageState? auth) async {
bool confirmed = false;
await showPopUp<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 {
auth != null
? auth.changeProcessText(S.of(context).wallet_list_removing_wallet(wallet.name))
: changeProcessText(S.of(context).wallet_list_removing_wallet(wallet.name));
await widget.walletListViewModel.remove(wallet);
} catch (e) {
auth != null
? auth.changeProcessText(
S.of(context).wallet_list_failed_to_remove(wallet.name, e.toString()),
)
: changeProcessText(
S.of(context).wallet_list_failed_to_remove(wallet.name, e.toString()),
);
}
}
auth?.close();
}
void changeProcessText(String text) {
@@ -294,16 +314,16 @@ class WalletListBodyState extends State<WalletListBody> {
}
ActionPane _actionPane(WalletListItem wallet) => ActionPane(
motion: const ScrollMotion(),
extentRatio: 0.3,
children: [
SlidableAction(
onPressed: (_) => _removeWallet(wallet),
backgroundColor: Colors.red,
foregroundColor: Colors.white,
icon: CupertinoIcons.delete,
label: S.of(context).delete,
),
],
);
motion: const ScrollMotion(),
extentRatio: 0.3,
children: [
SlidableAction(
onPressed: (_) => _removeWallet(wallet),
backgroundColor: Colors.red,
foregroundColor: Colors.white,
icon: CupertinoIcons.delete,
label: S.of(context).delete,
),
],
);
}
+5 -2
View File
@@ -4,6 +4,7 @@ import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/entities/qr_scanner.dart';
import 'package:cake_wallet/entities/contact_base.dart';
import 'package:cw_core/crypto_currency.dart';
enum AddressTextFieldOption { paste, qrCode, addressBook }
@@ -26,7 +27,8 @@ class AddressTextField extends StatelessWidget {
this.hintStyle,
this.validator,
this.onPushPasteButton,
this.onPushAddressBookButton});
this.onPushAddressBookButton,
this.selectedCurrency});
static const prefixIconWidth = 34.0;
static const prefixIconHeight = 34.0;
@@ -47,6 +49,7 @@ class AddressTextField extends StatelessWidget {
final FocusNode? focusNode;
final Function(BuildContext context)? onPushPasteButton;
final Function(BuildContext context)? onPushAddressBookButton;
final CryptoCurrency? selectedCurrency;
@override
Widget build(BuildContext context) {
@@ -207,7 +210,7 @@ class AddressTextField extends StatelessWidget {
Future<void> _presetAddressBookPicker(BuildContext context) async {
final contact = await Navigator.of(context, rootNavigator: true)
.pushNamed(Routes.pickerAddressBook);
.pushNamed(Routes.pickerAddressBook,arguments: selectedCurrency);
if (contact is ContactBase && contact.address != null) {
controller?.text = contact.address;
+4 -12
View File
@@ -1,4 +1,5 @@
import 'dart:ui';
import 'package:cake_wallet/src/widgets/section_divider.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/palette.dart';
@@ -76,10 +77,7 @@ class BaseAlertDialog extends StatelessWidget {
),
)),
),
Container(
width: 1,
color: Theme.of(context).dividerColor,
),
const SectionDivider(),
Expanded(
child: TextButton(
onPressed: actionRight,
@@ -140,10 +138,7 @@ class BaseAlertDialog extends StatelessWidget {
isDividerExists
? Padding(
padding: EdgeInsets.only(top: 16, bottom: 8),
child: Container(
height: 1,
color: Theme.of(context).dividerColor,
),
child: const SectionDivider(),
)
: Offstage(),
Padding(
@@ -152,10 +147,7 @@ class BaseAlertDialog extends StatelessWidget {
)
],
),
Container(
height: 1,
color: Theme.of(context).dividerColor,
),
const SectionDivider(),
actionButtons(context)
],
),
@@ -1,8 +1,8 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class StandartListRow extends StatelessWidget {
StandartListRow(
class ListRow extends StatelessWidget {
ListRow(
{required this.title,
required this.value,
this.titleFontSize = 14,
+4 -5
View File
@@ -4,7 +4,7 @@ import 'package:flutter/material.dart';
import 'package:cake_wallet/src/widgets/alert_background.dart';
import 'package:cake_wallet/src/widgets/alert_close_button.dart';
class Picker<Item extends Object> extends StatefulWidget {
class Picker<Item> extends StatefulWidget {
Picker({
required this.selectedAtIndex,
required this.items,
@@ -39,7 +39,7 @@ class Picker<Item extends Object> extends StatefulWidget {
_PickerState<Item> createState() => _PickerState<Item>(items, images, onItemSelected);
}
class _PickerState<Item> extends State<Picker> {
class _PickerState<Item> extends State<Picker<Item>> {
_PickerState(this.items, this.images, this.onItemSelected);
final Function(Item) onItemSelected;
@@ -59,7 +59,7 @@ class _PickerState<Item> extends State<Picker> {
images = [];
for (int i=0;i<widget.items.length;i++) {
if (widget.matchingCriteria?.call(widget.items[i], searchController.text) ?? true) {
items.add(widget.items[i] as Item);
items.add(widget.items[i]);
images.add(widget.images[i]);
}
}
@@ -238,8 +238,7 @@ class _PickerState<Item> extends State<Picker> {
child: Padding(
padding: EdgeInsets.only(left: image != null ? 12 : 0),
child: Text(
// What a hack (item as) ?
widget.displayItem?.call(item as Object) ?? item.toString(),
widget.displayItem?.call(item) ?? item.toString(),
style: TextStyle(
fontSize: 14,
fontFamily: 'Lato',
+13
View File
@@ -0,0 +1,13 @@
import 'package:flutter/material.dart';
class SectionDivider extends StatelessWidget {
const SectionDivider();
@override
Widget build(BuildContext context) {
return Container(
height: 1,
color: Theme.of(context).dividerColor,
);
}
}
+44 -57
View File
@@ -2,42 +2,42 @@ import 'dart:ui';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class StandardCheckbox extends StatefulWidget {
StandardCheckbox({
Key? key,
required this.value,
this.caption = '',
required this.onChanged})
: super(key: key);
class StandardCheckbox extends StatelessWidget {
StandardCheckbox(
{required this.value,
this.caption = '',
this.gradientBackground = false,
this.borderColor,
this.iconColor,
required this.onChanged});
final bool value;
final String caption;
final bool gradientBackground;
final Color? borderColor;
final Color? iconColor;
final Function(bool) onChanged;
@override
StandardCheckboxState createState() =>
StandardCheckboxState(value, caption, onChanged);
}
class StandardCheckboxState extends State<StandardCheckbox> {
StandardCheckboxState(this.value, this.caption, this.onChanged);
bool value;
String caption;
Function(bool) onChanged;
void changeValue(bool newValue) {
setState(() => value = newValue);
}
@override
Widget build(BuildContext context) {
final baseGradient = LinearGradient(colors: [
Theme.of(context).primaryTextTheme.subtitle1!.color!,
Theme.of(context).primaryTextTheme.subtitle1!.decorationColor!,
], begin: Alignment.centerLeft, end: Alignment.centerRight);
final boxBorder = Border.all(
color: borderColor ?? Theme.of(context).primaryTextTheme.caption!.color!, width: 1.0);
final checkedBoxDecoration = BoxDecoration(
gradient: gradientBackground ? baseGradient : null,
border: gradientBackground ? null : boxBorder,
borderRadius: BorderRadius.all(Radius.circular(8.0)));
final uncheckedBoxDecoration =
BoxDecoration(border: boxBorder, borderRadius: BorderRadius.all(Radius.circular(8.0)));
return GestureDetector(
onTap: () {
value = !value;
onChanged(value);
setState(() {});
},
onTap: () => onChanged(!value),
child: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
@@ -45,38 +45,25 @@ class StandardCheckboxState extends State<StandardCheckbox> {
Container(
height: 24.0,
width: 24.0,
decoration: BoxDecoration(
border: Border.all(
color: Theme.of(context)
.primaryTextTheme!
.caption!
.color!,
width: 1.0),
borderRadius: BorderRadius.all(
Radius.circular(8.0)),
color: Theme.of(context).backgroundColor),
decoration: value ? checkedBoxDecoration : uncheckedBoxDecoration,
child: value
? Icon(
Icons.check,
color: Colors.blue,
size: 20.0,
)
: Offstage(),
? Icon(
Icons.check,
color: iconColor ?? Colors.blue,
size: 20.0,
)
: Offstage(),
),
if (caption.isNotEmpty) Padding(
padding: EdgeInsets.only(left: 10),
child: Text(
caption,
style: TextStyle(
fontSize: 16.0,
color: Theme.of(context)
.primaryTextTheme!
.headline6!
.color!),
)
)
if (caption.isNotEmpty)
Padding(
padding: EdgeInsets.only(left: 10),
child: Text(
caption,
style: TextStyle(
fontSize: 16.0, color: Theme.of(context).primaryTextTheme!.headline6!.color!),
))
],
),
);
}
}
}
+6 -4
View File
@@ -1,6 +1,6 @@
import 'package:cake_wallet/palette.dart';
import 'package:cake_wallet/src/widgets/standart_list_card.dart';
import 'package:cake_wallet/src/widgets/standart_list_status_row.dart';
import 'package:cake_wallet/src/widgets/standard_list_card.dart';
import 'package:cake_wallet/src/widgets/standard_list_status_row.dart';
import 'package:flutter/material.dart';
class StandardListRow extends StatelessWidget {
@@ -123,6 +123,7 @@ class SectionStandardList extends StatelessWidget {
required this.itemBuilder,
required this.sectionCount,
required BuildContext context,
this.dividerPadding = const EdgeInsets.only(left: 24),
this.themeColor,
this.dividerThemeColor,
this.sectionTitleBuilder,
@@ -149,6 +150,7 @@ class SectionStandardList extends StatelessWidget {
final List<Widget> totalRows;
final Color? themeColor;
final Color? dividerThemeColor;
final EdgeInsets dividerPadding;
List<Widget> transform(
bool hasTopSeparator,
@@ -178,7 +180,7 @@ class SectionStandardList extends StatelessWidget {
items.add(sectionIndex + 1 != sectionCount
? SectionHeaderListRow()
: StandardListSeparator(padding: EdgeInsets.only(left: 24)));
: StandardListSeparator(padding: dividerPadding));
}
return items;
@@ -215,7 +217,7 @@ class SectionStandardList extends StatelessWidget {
return Container();
}
if (row is StandartListStatusRow || row is TradeDatailsStandartListCard) {
if (row is StandardListStatusRow || row is TradeDetailsStandardListCard) {
return Container();
}
@@ -2,8 +2,8 @@ import 'package:cake_wallet/palette.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/themes/theme_base.dart';
class TradeDatailsStandartListCard extends StatelessWidget {
TradeDatailsStandartListCard(
class TradeDetailsStandardListCard extends StatelessWidget {
TradeDetailsStandardListCard(
{required this.id,
required this.create,
required this.pair,
@@ -3,8 +3,8 @@ import 'package:cake_wallet/src/screens/dashboard/widgets/sync_indicator_icon.da
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class StandartListStatusRow extends StatelessWidget {
StandartListStatusRow({required this.title, required this.value});
class StandardListStatusRow extends StatelessWidget {
StandardListStatusRow({required this.title, required this.value});
final String title;
final String value;
@@ -1,17 +1,17 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class StandartSwitch extends StatefulWidget {
const StandartSwitch({required this.value, required this.onTaped});
class StandardSwitch extends StatefulWidget {
const StandardSwitch({required this.value, required this.onTaped});
final bool value;
final VoidCallback onTaped;
@override
StandartSwitchState createState() => StandartSwitchState();
StandardSwitchState createState() => StandardSwitchState();
}
class StandartSwitchState extends State<StandartSwitch> {
class StandardSwitchState extends State<StandardSwitch> {
@override
Widget build(BuildContext context) {
return GestureDetector(
+1 -4
View File
@@ -4,7 +4,7 @@ part 'authentication_store.g.dart';
class AuthenticationStore = AuthenticationStoreBase with _$AuthenticationStore;
enum AuthenticationState { uninitialized, installed, allowed, denied }
enum AuthenticationState { uninitialized, installed, allowed }
abstract class AuthenticationStoreBase with Store {
AuthenticationStoreBase() : state = AuthenticationState.uninitialized;
@@ -17,7 +17,4 @@ abstract class AuthenticationStoreBase with Store {
@action
void allowed() => state = AuthenticationState.allowed;
@action
void denied() => state = AuthenticationState.denied;
}
+34 -9
View File
@@ -8,12 +8,11 @@ part'trade_filter_store.g.dart';
class TradeFilterStore = TradeFilterStoreBase with _$TradeFilterStore;
abstract class TradeFilterStoreBase with Store {
TradeFilterStoreBase(
{this.displayXMRTO = true,
this.displayChangeNow = true,
this.displayMorphToken = true,
this.displaySimpleSwap = true,
});
TradeFilterStoreBase() : displayXMRTO = true,
displayChangeNow = true,
displaySideShift = true,
displayMorphToken = true,
displaySimpleSwap = true;
@observable
bool displayXMRTO;
@@ -21,26 +20,50 @@ abstract class TradeFilterStoreBase with Store {
@observable
bool displayChangeNow;
@observable
bool displaySideShift;
@observable
bool displayMorphToken;
@observable
bool displaySimpleSwap;
@computed
bool get displayAllTrades => displayChangeNow && displaySideShift && displaySimpleSwap;
@action
void toggleDisplayExchange(ExchangeProviderDescription provider) {
switch (provider) {
case ExchangeProviderDescription.changeNow:
displayChangeNow = !displayChangeNow;
break;
case ExchangeProviderDescription.sideShift:
displaySideShift = !displaySideShift;
break;
case ExchangeProviderDescription.simpleSwap:
displaySimpleSwap = !displaySimpleSwap;
break;
case ExchangeProviderDescription.xmrto:
displayXMRTO = !displayXMRTO;
break;
case ExchangeProviderDescription.morphToken:
displayMorphToken = !displayMorphToken;
break;
case ExchangeProviderDescription.simpleSwap:
displaySimpleSwap = !displaySimpleSwap;
case ExchangeProviderDescription.all:
if (displayAllTrades) {
displayChangeNow = false;
displaySideShift = false;
displayXMRTO = false;
displayMorphToken = false;
displaySimpleSwap = false;
} else {
displayChangeNow = true;
displaySideShift = true;
displayXMRTO = true;
displayMorphToken = true;
displaySimpleSwap = true;
}
break;
}
}
@@ -48,13 +71,15 @@ abstract class TradeFilterStoreBase with Store {
List<TradeListItem> filtered({required List<TradeListItem> trades, required WalletBase wallet}) {
final _trades =
trades.where((item) => item.trade.walletId == wallet.id).toList();
final needToFilter = !displayChangeNow || !displayXMRTO || !displayMorphToken || !displaySimpleSwap;
final needToFilter = !displayAllTrades;
return needToFilter
? _trades
.where((item) =>
(displayXMRTO &&
item.trade.provider == ExchangeProviderDescription.xmrto) ||
(displaySideShift &&
item.trade.provider == ExchangeProviderDescription.sideShift) ||
(displayChangeNow &&
item.trade.provider ==
ExchangeProviderDescription.changeNow) ||
+4 -9
View File
@@ -31,13 +31,8 @@ abstract class TradesStoreBase with Store {
void setTrade(Trade trade) => this.trade = trade;
@action
Future<void> updateTradeList() async {
if (trade == null) {
return;
}
trades = tradesSource.values.map((trade) => TradeListItem(
trade: trade!,
settingsStore: settingsStore)).toList();
}
Future<void> updateTradeList() async => trades =
tradesSource.values.map((trade) => TradeListItem(
trade: trade,
settingsStore: settingsStore)).toList();
}
@@ -1,6 +1,8 @@
import 'package:mobx/mobx.dart';
import 'package:cw_core/transaction_direction.dart';
import 'package:cake_wallet/view_model/dashboard/transaction_list_item.dart';
import 'package:cake_wallet/view_model/dashboard/filter_item.dart';
import 'package:cake_wallet/generated/i18n.dart';
part 'transaction_filter_store.g.dart';
@@ -8,8 +10,8 @@ class TransactionFilterStore = TransactionFilterStoreBase
with _$TransactionFilterStore;
abstract class TransactionFilterStoreBase with Store {
TransactionFilterStoreBase(
{this.displayIncoming = true, this.displayOutgoing = true});
TransactionFilterStoreBase() : displayIncoming = true,
displayOutgoing = true;
@observable
bool displayIncoming;
@@ -23,11 +25,31 @@ abstract class TransactionFilterStoreBase with Store {
@observable
DateTime? endDate;
@action
void toggleIncoming() => displayIncoming = !displayIncoming;
@computed
bool get displayAll => displayIncoming && displayOutgoing;
@action
void toggleOutgoing() => displayOutgoing = !displayOutgoing;
void toggleAll() {
if (displayAll) {
displayOutgoing = false;
displayIncoming = false;
} else {
displayOutgoing = true;
displayIncoming = true;
}
}
@action
void toggleIncoming() {
displayIncoming = !displayIncoming;
}
@action
void toggleOutgoing() {
displayOutgoing = !displayOutgoing;
}
@action
void changeStartDate(DateTime date) => startDate = date;
@@ -37,8 +59,7 @@ abstract class TransactionFilterStoreBase with Store {
List<TransactionListItem> filtered({required List<TransactionListItem> transactions}) {
var _transactions = <TransactionListItem>[];
final needToFilter = !displayOutgoing ||
!displayIncoming ||
final needToFilter = !displayAll ||
(startDate != null && endDate != null);
if (needToFilter) {
@@ -50,7 +71,7 @@ abstract class TransactionFilterStoreBase with Store {
&& (endDate?.isAfter(item.transaction.date) ?? false);
}
if (allowed && (!displayOutgoing || !displayIncoming)) {
if (allowed && (!displayAll)) {
allowed = (displayOutgoing &&
item.transaction.direction ==
TransactionDirection.outgoing) ||
+171 -64
View File
@@ -1,9 +1,9 @@
import 'package:cake_wallet/bitcoin/bitcoin.dart';
import 'package:cake_wallet/entities/pin_code_required_duration.dart';
import 'package:cake_wallet/entities/preferences_key.dart';
import 'package:cw_core/transaction_priority.dart';
import 'package:cake_wallet/themes/theme_base.dart';
import 'package:cake_wallet/themes/theme_list.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:hive/hive.dart';
import 'package:mobx/mobx.dart';
@@ -17,7 +17,7 @@ import 'package:cake_wallet/entities/fiat_currency.dart';
import 'package:cw_core/node.dart';
import 'package:cake_wallet/monero/monero.dart';
import 'package:cake_wallet/entities/action_list_display_mode.dart';
import 'package:cake_wallet/.secrets.g.dart' as secrets;
import 'package:cake_wallet/entities/fiat_api_mode.dart';
part 'settings_store.g.dart';
@@ -29,6 +29,7 @@ abstract class SettingsStoreBase with Store {
required FiatCurrency initialFiatCurrency,
required BalanceDisplayMode initialBalanceDisplayMode,
required bool initialSaveRecipientAddress,
required FiatApiMode initialFiatMode,
required bool initialAllowBiometricalAuthentication,
required bool initialExchangeEnabled,
required ThemeBase initialTheme,
@@ -40,13 +41,17 @@ abstract class SettingsStoreBase with Store {
required this.shouldShowYatPopup,
required this.isBitcoinBuyEnabled,
required this.actionlistDisplayMode,
required this.pinTimeOutDuration,
TransactionPriority? initialBitcoinTransactionPriority,
TransactionPriority? initialMoneroTransactionPriority})
TransactionPriority? initialMoneroTransactionPriority,
TransactionPriority? initialHavenTransactionPriority,
TransactionPriority? initialLitecoinTransactionPriority})
: nodes = ObservableMap<WalletType, Node>.of(nodes),
_sharedPreferences = sharedPreferences,
fiatCurrency = initialFiatCurrency,
balanceDisplayMode = initialBalanceDisplayMode,
shouldSaveRecipientAddress = initialSaveRecipientAddress,
fiatApiMode = initialFiatMode,
allowBiometricalAuthentication = initialAllowBiometricalAuthentication,
disableExchange = initialExchangeEnabled,
currentTheme = initialTheme,
@@ -63,6 +68,14 @@ abstract class SettingsStoreBase with Store {
priority[WalletType.bitcoin] = initialBitcoinTransactionPriority;
}
if (initialHavenTransactionPriority != null) {
priority[WalletType.haven] = initialHavenTransactionPriority;
}
if (initialLitecoinTransactionPriority != null) {
priority[WalletType.litecoin] = initialLitecoinTransactionPriority;
}
reaction(
(_) => fiatCurrency,
(FiatCurrency fiatCurrency) => sharedPreferences.setString(
@@ -74,11 +87,25 @@ abstract class SettingsStoreBase with Store {
.setBool(PreferencesKey.shouldShowYatPopup, shouldShowYatPopup));
priority.observe((change) {
final key = change.key == WalletType.monero
? PreferencesKey.moneroTransactionPriority
: PreferencesKey.bitcoinTransactionPriority;
final String? key;
switch (change.key) {
case WalletType.monero:
key = PreferencesKey.moneroTransactionPriority;
break;
case WalletType.bitcoin:
key = PreferencesKey.bitcoinTransactionPriority;
break;
case WalletType.litecoin:
key = PreferencesKey.litecoinTransactionPriority;
break;
case WalletType.haven:
key = PreferencesKey.havenTransactionPriority;
break;
default:
key = null;
}
if (change.newValue != null) {
if (change.newValue != null && key != null) {
sharedPreferences.setInt(key, change.newValue!.serialize());
}
});
@@ -89,6 +116,11 @@ abstract class SettingsStoreBase with Store {
PreferencesKey.shouldSaveRecipientAddressKey,
shouldSaveRecipientAddress));
reaction(
(_) => fiatApiMode,
(FiatApiMode mode) => sharedPreferences.setInt(
PreferencesKey.currentFiatApiModeKey, mode.serialize()));
reaction(
(_) => currentTheme,
(ThemeBase theme) =>
@@ -110,14 +142,24 @@ abstract class SettingsStoreBase with Store {
(String languageCode) => sharedPreferences.setString(
PreferencesKey.currentLanguageCode, languageCode));
reaction(
(_) => pinTimeOutDuration,
(PinCodeRequiredDuration pinCodeInterval) => sharedPreferences.setInt(
PreferencesKey.pinTimeOutDuration, pinCodeInterval.value));
reaction(
(_) => balanceDisplayMode,
(BalanceDisplayMode mode) => sharedPreferences.setInt(
PreferencesKey.currentBalanceDisplayModeKey, mode.serialize()));
reaction(
(_) => disableExchange,
(bool disableExchange) => sharedPreferences.setBool(
PreferencesKey.disableExchangeKey, disableExchange));
this
.nodes
.observe((change) {
.observe((change) {
if (change.newValue != null && change.key != null) {
_saveCurrentNode(change.newValue!, change.key!);
}
@@ -126,6 +168,7 @@ abstract class SettingsStoreBase with Store {
static const defaultPinLength = 4;
static const defaultActionsMode = 11;
static const defaultPinCodeTimeOutDuration = PinCodeRequiredDuration.tenminutes;
@observable
FiatCurrency fiatCurrency;
@@ -139,6 +182,9 @@ abstract class SettingsStoreBase with Store {
@observable
BalanceDisplayMode balanceDisplayMode;
@observable
FiatApiMode fiatApiMode;
@observable
bool shouldSaveRecipientAddress;
@@ -154,6 +200,9 @@ abstract class SettingsStoreBase with Store {
@observable
int pinCodeLength;
@observable
PinCodeRequiredDuration pinTimeOutDuration;
@computed
ThemeData get theme => currentTheme.themeData;
@@ -190,39 +239,48 @@ abstract class SettingsStoreBase with Store {
static Future<SettingsStore> load(
{required Box<Node> nodeSource,
required bool isBitcoinBuyEnabled,
TransactionPriority? initialMoneroTransactionPriority,
TransactionPriority? initialBitcoinTransactionPriority,
FiatCurrency initialFiatCurrency = FiatCurrency.usd,
BalanceDisplayMode initialBalanceDisplayMode =
BalanceDisplayMode.availableBalance}) async {
if (initialBitcoinTransactionPriority == null) {
initialBitcoinTransactionPriority = bitcoin?.getMediumTransactionPriority();
}
if (initialMoneroTransactionPriority == null) {
initialMoneroTransactionPriority = monero?.getDefaultTransactionPriority();
}
final sharedPreferences = await getIt.getAsync<SharedPreferences>();
final currentFiatCurrency = FiatCurrency.deserialize(raw:
sharedPreferences.getString(PreferencesKey.currentFiatCurrencyKey)!);
final savedMoneroTransactionPriority =
TransactionPriority? moneroTransactionPriority =
monero?.deserializeMoneroTransactionPriority(
raw: sharedPreferences
.getInt(PreferencesKey.moneroTransactionPriority)!);
final savedBitcoinTransactionPriority =
TransactionPriority? bitcoinTransactionPriority =
bitcoin?.deserializeBitcoinTransactionPriority(sharedPreferences
.getInt(PreferencesKey.bitcoinTransactionPriority)!);
final moneroTransactionPriority =
savedMoneroTransactionPriority ?? initialMoneroTransactionPriority;
final bitcoinTransactionPriority =
savedBitcoinTransactionPriority ?? initialBitcoinTransactionPriority;
TransactionPriority? havenTransactionPriority;
TransactionPriority? litecoinTransactionPriority;
if (sharedPreferences.getInt(PreferencesKey.havenTransactionPriority) != null) {
havenTransactionPriority = monero?.deserializeMoneroTransactionPriority(
raw: sharedPreferences.getInt(PreferencesKey.havenTransactionPriority)!);
}
if (sharedPreferences.getInt(PreferencesKey.litecoinTransactionPriority) != null) {
litecoinTransactionPriority = bitcoin?.deserializeLitecoinTransactionPriority(
sharedPreferences.getInt(PreferencesKey.litecoinTransactionPriority)!);
}
moneroTransactionPriority ??= monero?.getDefaultTransactionPriority();
bitcoinTransactionPriority ??= bitcoin?.getMediumTransactionPriority();
havenTransactionPriority ??= monero?.getDefaultTransactionPriority();
litecoinTransactionPriority ??= bitcoin?.getLitecoinTransactionPriorityMedium();
final currentBalanceDisplayMode = BalanceDisplayMode.deserialize(
raw: sharedPreferences
.getInt(PreferencesKey.currentBalanceDisplayModeKey)!);
// FIX-ME: Check for which default value we should have here
final shouldSaveRecipientAddress =
sharedPreferences.getBool(PreferencesKey.shouldSaveRecipientAddressKey) ?? false;
final currentFiatApiMode = FiatApiMode.deserialize(
raw: sharedPreferences
.getInt(PreferencesKey.currentFiatApiModeKey) ?? FiatApiMode.enabled.raw);
final allowBiometricalAuthentication = sharedPreferences
.getBool(PreferencesKey.allowBiometricalAuthenticationKey) ??
false;
@@ -234,13 +292,17 @@ abstract class SettingsStoreBase with Store {
: ThemeType.bright.index;
final savedTheme = ThemeList.deserialize(
raw: sharedPreferences.getInt(PreferencesKey.currentTheme) ??
legacyTheme ??
0);
legacyTheme);
final actionListDisplayMode = ObservableList<ActionListDisplayMode>();
actionListDisplayMode.addAll(deserializeActionlistDisplayModes(
sharedPreferences.getInt(PreferencesKey.displayActionListModeKey) ??
defaultActionsMode));
var pinLength = sharedPreferences.getInt(PreferencesKey.currentPinLength);
final timeOutDuration = sharedPreferences.getInt(PreferencesKey.pinTimeOutDuration);
final pinCodeTimeOutDuration = timeOutDuration != null
? PinCodeRequiredDuration.deserialize(raw: timeOutDuration)
: defaultPinCodeTimeOutDuration;
// If no value
if (pinLength == null || pinLength == 0) {
pinLength = defaultPinLength;
@@ -281,7 +343,7 @@ abstract class SettingsStoreBase with Store {
if (havenNode != null) {
nodes[WalletType.haven] = havenNode;
}
return SettingsStore(
sharedPreferences: sharedPreferences,
nodes: nodes,
@@ -290,59 +352,104 @@ abstract class SettingsStoreBase with Store {
initialFiatCurrency: currentFiatCurrency,
initialBalanceDisplayMode: currentBalanceDisplayMode,
initialSaveRecipientAddress: shouldSaveRecipientAddress,
initialFiatMode: currentFiatApiMode,
initialAllowBiometricalAuthentication: allowBiometricalAuthentication,
initialExchangeEnabled: disableExchange,
initialTheme: savedTheme,
actionlistDisplayMode: actionListDisplayMode,
initialPinLength: pinLength,
pinTimeOutDuration: pinCodeTimeOutDuration,
initialLanguageCode: savedLanguageCode,
initialMoneroTransactionPriority: moneroTransactionPriority,
initialBitcoinTransactionPriority: bitcoinTransactionPriority,
initialHavenTransactionPriority: havenTransactionPriority,
initialLitecoinTransactionPriority: litecoinTransactionPriority,
shouldShowYatPopup: shouldShowYatPopup);
}
// FIX-ME: Dead code
Future<void> reload({required Box<Node> nodeSource}) async {
//Future<void> reload(
// {required Box<Node> nodeSource,
// FiatCurrency initialFiatCurrency = FiatCurrency.usd,
// TransactionPriority? initialMoneroTransactionPriority,
// TransactionPriority? initialBitcoinTransactionPriority,
// BalanceDisplayMode initialBalanceDisplayMode =
// BalanceDisplayMode.availableBalance}) async {
// if (initialBitcoinTransactionPriority == null) {
// initialBitcoinTransactionPriority = bitcoin?.getMediumTransactionPriority();
// }
final sharedPreferences = await getIt.getAsync<SharedPreferences>();
// if (initialMoneroTransactionPriority == null) {
// initialMoneroTransactionPriority = monero?.getDefaultTransactionPriority();
// }
fiatCurrency = FiatCurrency.deserialize(
raw: sharedPreferences.getString(PreferencesKey.currentFiatCurrencyKey)!);
// final isBitcoinBuyEnabled = (secrets.wyreSecretKey?.isNotEmpty ?? false) &&
// (secrets.wyreApiKey?.isNotEmpty ?? false) &&
// (secrets.wyreAccountId?.isNotEmpty ?? false);
priority[WalletType.monero] = monero?.deserializeMoneroTransactionPriority(
raw: sharedPreferences.getInt(PreferencesKey.moneroTransactionPriority)!) ??
priority[WalletType.monero]!;
priority[WalletType.bitcoin] = bitcoin?.deserializeBitcoinTransactionPriority(
sharedPreferences.getInt(PreferencesKey.moneroTransactionPriority)!) ??
priority[WalletType.bitcoin]!;
// final settings = await SettingsStoreBase.load(
// nodeSource: nodeSource,
// isBitcoinBuyEnabled: isBitcoinBuyEnabled,
// initialBalanceDisplayMode: initialBalanceDisplayMode,
// initialFiatCurrency: initialFiatCurrency,
// initialMoneroTransactionPriority: initialMoneroTransactionPriority,
// initialBitcoinTransactionPriority: initialBitcoinTransactionPriority);
// fiatCurrency = settings.fiatCurrency;
// actionlistDisplayMode = settings.actionlistDisplayMode;
// priority[WalletType.monero] = initialMoneroTransactionPriority;
// priority[WalletType.bitcoin] = initialBitcoinTransactionPriority;
// balanceDisplayMode = settings.balanceDisplayMode;
// shouldSaveRecipientAddress = settings.shouldSaveRecipientAddress;
// allowBiometricalAuthentication = settings.allowBiometricalAuthentication;
// currentTheme = settings.currentTheme;
// pinCodeLength = settings.pinCodeLength;
// languageCode = settings.languageCode;
// appVersion = settings.appVersion;
// shouldShowYatPopup = settings.shouldShowYatPopup;
//}
if (sharedPreferences.getInt(PreferencesKey.havenTransactionPriority) != null) {
priority[WalletType.haven] = monero?.deserializeMoneroTransactionPriority(
raw: sharedPreferences.getInt(PreferencesKey.havenTransactionPriority)!) ??
priority[WalletType.haven]!;
}
if (sharedPreferences.getInt(PreferencesKey.litecoinTransactionPriority) != null) {
priority[WalletType.litecoin] = bitcoin?.deserializeLitecoinTransactionPriority(
sharedPreferences.getInt(PreferencesKey.litecoinTransactionPriority)!) ??
priority[WalletType.litecoin]!;
}
balanceDisplayMode = BalanceDisplayMode.deserialize(
raw: sharedPreferences
.getInt(PreferencesKey.currentBalanceDisplayModeKey)!);
shouldSaveRecipientAddress =
sharedPreferences.getBool(PreferencesKey.shouldSaveRecipientAddressKey) ?? shouldSaveRecipientAddress;
allowBiometricalAuthentication = sharedPreferences
.getBool(PreferencesKey.allowBiometricalAuthenticationKey) ??
allowBiometricalAuthentication;
disableExchange = sharedPreferences.getBool(PreferencesKey.disableExchangeKey) ?? disableExchange;
final legacyTheme =
(sharedPreferences.getBool(PreferencesKey.isDarkThemeLegacy) ?? false)
? ThemeType.dark.index
: ThemeType.bright.index;
currentTheme = ThemeList.deserialize(
raw: sharedPreferences.getInt(PreferencesKey.currentTheme) ??
legacyTheme);
actionlistDisplayMode = ObservableList<ActionListDisplayMode>();
actionlistDisplayMode.addAll(deserializeActionlistDisplayModes(
sharedPreferences.getInt(PreferencesKey.displayActionListModeKey) ??
defaultActionsMode));
var pinLength = sharedPreferences.getInt(PreferencesKey.currentPinLength);
// If no value
if (pinLength == null || pinLength == 0) {
pinLength = pinCodeLength;
}
pinCodeLength = pinLength;
languageCode = sharedPreferences.getString(PreferencesKey.currentLanguageCode) ?? languageCode;
shouldShowYatPopup = sharedPreferences.getBool(PreferencesKey.shouldShowYatPopup) ?? shouldShowYatPopup;
final nodeId = sharedPreferences.getInt(PreferencesKey.currentNodeIdKey);
final bitcoinElectrumServerId = sharedPreferences
.getInt(PreferencesKey.currentBitcoinElectrumSererIdKey);
final litecoinElectrumServerId = sharedPreferences
.getInt(PreferencesKey.currentLitecoinElectrumSererIdKey);
final havenNodeId = sharedPreferences
.getInt(PreferencesKey.currentHavenNodeIdKey);
final moneroNode = nodeSource.get(nodeId);
final bitcoinElectrumServer = nodeSource.get(bitcoinElectrumServerId);
final litecoinElectrumServer = nodeSource.get(litecoinElectrumServerId);
final havenNode = nodeSource.get(havenNodeId);
if (moneroNode != null) {
nodes[WalletType.monero] = moneroNode;
}
if (bitcoinElectrumServer != null) {
nodes[WalletType.bitcoin] = bitcoinElectrumServer;
}
if (litecoinElectrumServer != null) {
nodes[WalletType.litecoin] = litecoinElectrumServer;
}
if (havenNode != null) {
nodes[WalletType.haven] = havenNode;
}
}
Future<void> _saveCurrentNode(Node node, WalletType walletType) async {
switch (walletType) {
+6 -3
View File
@@ -1,22 +1,25 @@
class PaymentRequest {
PaymentRequest(this.address, this.amount, this.note);
PaymentRequest(this.address, this.amount, this.note, this.scheme);
factory PaymentRequest.fromUri(Uri uri) {
factory PaymentRequest.fromUri(Uri? uri) {
var address = "";
var amount = "";
var note = "";
var scheme = "";
if (uri != null) {
address = uri.path;
amount = uri.queryParameters['tx_amount'] ?? uri.queryParameters['amount'] ?? "";
note = uri.queryParameters['tx_description']
?? uri.queryParameters['message'] ?? "";
scheme = uri.scheme;
}
return PaymentRequest(address, amount, note);
return PaymentRequest(address, amount, note, scheme);
}
final String address;
final String amount;
final String note;
final String scheme;
}
+65
View File
@@ -0,0 +1,65 @@
import 'package:flutter/material.dart';
import 'package:cake_wallet/main.dart';
class RouteAwareWidget extends StatefulWidget {
RouteAwareWidget(
{required this.child,
this.pushToWidget,
this.pushToNextWidget,
this.popWidget,
this.popNextWidget});
final Widget child;
final Function()? pushToWidget;
final Function()? pushToNextWidget;
final Function()? popWidget;
final Function()? popNextWidget;
@override
State<RouteAwareWidget> createState() => RouteAwareWidgetState();
}
class RouteAwareWidgetState extends State<RouteAwareWidget> with RouteAware {
@override
void didChangeDependencies() {
super.didChangeDependencies();
routeObserver.subscribe(this, ModalRoute.of(context) as PageRoute);
}
@override
void dispose() {
routeObserver.unsubscribe(this);
super.dispose();
}
@override
void didPush() {
if (widget.pushToWidget != null) {
widget.pushToWidget!();
}
}
@override
void didPushNext() {
if (widget.pushToNextWidget != null) {
widget.pushToNextWidget!();
}
}
@override
void didPop() {
if (widget.popWidget != null) {
widget.popWidget!();
}
}
@override
void didPopNext() {
if (widget.popNextWidget != null) {
widget.popNextWidget!();
}
}
@override
Widget build(BuildContext context) => widget.child;
}
@@ -0,0 +1,56 @@
import 'package:cake_wallet/entities/fiat_api_mode.dart';
import 'package:cake_wallet/store/settings_store.dart';
import 'package:cake_wallet/view_model/settings/switcher_list_item.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:mobx/mobx.dart';
import 'package:cake_wallet/generated/i18n.dart';
part 'advanced_privacy_settings_view_model.g.dart';
class AdvancedPrivacySettingsViewModel = AdvancedPrivacySettingsViewModelBase
with _$AdvancedPrivacySettingsViewModel;
abstract class AdvancedPrivacySettingsViewModelBase with Store {
AdvancedPrivacySettingsViewModelBase(this.type, this._settingsStore)
: _addCustomNode = false {
settings = [
SwitcherListItem(
title: S.current.disable_fiat,
value: () => _settingsStore.fiatApiMode == FiatApiMode.disabled,
onValueChange: (_, bool value) => setFiatMode(value),
),
SwitcherListItem(
title: S.current.disable_exchange,
value: () => _settingsStore.disableExchange,
onValueChange: (_, bool value) {
_settingsStore.disableExchange = value;
},
),
SwitcherListItem(
title: S.current.add_custom_node,
value: () => _addCustomNode,
onValueChange: (_, bool value) => _addCustomNode = value,
),
];
}
late List<SwitcherListItem> settings;
@observable
bool _addCustomNode = false;
final WalletType type;
final SettingsStore _settingsStore;
@computed
bool get addCustomNode => _addCustomNode;
@action
void setFiatMode(bool value) {
if (value) {
_settingsStore.fiatApiMode = FiatApiMode.disabled;
return;
}
_settingsStore.fiatApiMode = FiatApiMode.enabled;
}
}
+13 -6
View File
@@ -14,10 +14,12 @@ part 'auth_view_model.g.dart';
class AuthViewModel = AuthViewModelBase with _$AuthViewModel;
abstract class AuthViewModelBase with Store {
AuthViewModelBase(this._authService, this._sharedPreferences,
this._settingsStore, this._biometricAuth)
AuthViewModelBase(
this._authService, this._sharedPreferences, this._settingsStore, this._biometricAuth)
: _failureCounter = 0,
state = InitialExecutionState();
state = InitialExecutionState() {
reaction((_) => state, _saveLastAuthTime);
}
static const maxFailedLogins = 3;
static const banTimeout = 180; // 3 minutes
@@ -28,8 +30,7 @@ abstract class AuthViewModelBase with Store {
int get pinLength => _settingsStore.pinCodeLength;
bool get isBiometricalAuthenticationAllowed =>
_settingsStore.allowBiometricalAuthentication;
bool get isBiometricalAuthenticationAllowed => _settingsStore.allowBiometricalAuthentication;
@observable
int _failureCounter;
@@ -114,8 +115,14 @@ abstract class AuthViewModelBase with Store {
state = ExecutedSuccessfullyState();
}
}
} catch(e) {
} catch (e) {
state = FailureState(e.toString());
}
}
void _saveLastAuthTime(ExecutionState state) {
if (state is ExecutedSuccessfullyState) {
_authService.saveLastAuthTime();
}
}
}
@@ -7,27 +7,26 @@ import 'package:mobx/mobx.dart';
import 'package:cake_wallet/entities/contact_record.dart';
import 'package:cake_wallet/entities/contact.dart';
import 'package:cake_wallet/utils/mobx.dart';
import 'package:cw_core/crypto_currency.dart';
part 'contact_list_view_model.g.dart';
class ContactListViewModel = ContactListViewModelBase
with _$ContactListViewModel;
class ContactListViewModel = ContactListViewModelBase with _$ContactListViewModel;
abstract class ContactListViewModelBase with Store {
ContactListViewModelBase(this.contactSource, this.walletInfoSource)
ContactListViewModelBase(this.contactSource, this.walletInfoSource, this._currency)
: contacts = ObservableList<ContactRecord>(),
walletContacts = [] {
walletInfoSource.values.forEach((info) {
if (info.addresses?.isNotEmpty ?? false) {
info.addresses?.forEach((address, label) {
final name = label.isNotEmpty
? info.name + ' ($label)'
: info.name;
final name = label.isNotEmpty ? info.name + ' ($label)' : info.name;
walletContacts.add(WalletContact(
address,
name,
walletTypeToCryptoCurrency(info.type)));
address,
name,
walletTypeToCryptoCurrency(info.type),
));
});
}
});
@@ -41,7 +40,18 @@ abstract class ContactListViewModelBase with Store {
final Box<WalletInfo> walletInfoSource;
final ObservableList<ContactRecord> contacts;
final List<WalletContact> walletContacts;
final CryptoCurrency? _currency;
StreamSubscription<BoxEvent>? _subscription;
bool get isEditable => _currency == null;
Future<void> delete(ContactRecord contact) async => contact.original.delete();
@computed
List<ContactRecord> get contactsToShow =>
contacts.where((element) => _currency == null || element.type == _currency).toList();
@computed
List<WalletContact> get walletContactsToShow =>
walletContacts.where((element) => _currency == null || element.type == _currency).toList();
}
@@ -1,3 +1,4 @@
import 'package:cake_wallet/entities/fiat_api_mode.dart';
import 'package:cw_core/transaction_history.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/balance.dart';
@@ -10,7 +11,6 @@ import 'package:cake_wallet/entities/calculate_fiat_amount.dart';
import 'package:cake_wallet/store/app_store.dart';
import 'package:cake_wallet/store/settings_store.dart';
import 'package:cake_wallet/store/dashboard/fiat_conversion_store.dart';
import 'package:flutter/cupertino.dart';
import 'package:mobx/mobx.dart';
part 'balance_view_model.g.dart';
@@ -71,6 +71,9 @@ abstract class BalanceViewModelBase with Store {
@computed
BalanceDisplayMode get savedDisplayMode => settingsStore.balanceDisplayMode;
@computed
bool get isFiatDisabled => settingsStore.fiatApiMode == FiatApiMode.disabled;
@computed
String get asset {
final typeFormatted = walletTypeToString(appStore.wallet!.type);
@@ -180,8 +183,8 @@ abstract class BalanceViewModelBase with Store {
return MapEntry(key, BalanceRecord(
availableBalance: '---',
additionalBalance: '---',
fiatAdditionalBalance: '---',
fiatAvailableBalance: '---',
fiatAdditionalBalance: isFiatDisabled ? '' : '---',
fiatAvailableBalance: isFiatDisabled ? '' : '---',
asset: key,
formattedAssetTitle: _formatterAsset(key)));
}
@@ -192,17 +195,17 @@ abstract class BalanceViewModelBase with Store {
// throw Exception('Price is null for: $key');
// }
final additionalFiatBalance = fiatCurrency.toString()
+ ' '
final additionalFiatBalance = isFiatDisabled ? '' : (fiatCurrency.toString()
+ ' '
+ _getFiatBalance(
price: price,
cryptoAmount: value.formattedAdditionalBalance);
cryptoAmount: value.formattedAdditionalBalance));
final availableFiatBalance = fiatCurrency.toString()
+ ' '
final availableFiatBalance = isFiatDisabled ? '' : (fiatCurrency.toString()
+ ' '
+ _getFiatBalance(
price: price,
cryptoAmount: value.formattedAvailableBalance);
cryptoAmount: value.formattedAvailableBalance));
return MapEntry(key, BalanceRecord(
availableBalance: value.formattedAvailableBalance,
@@ -58,25 +58,44 @@ abstract class DashboardViewModelBase with Store {
isShowThirdYatIntroduction = false,
filterItems = {
S.current.transactions: [
FilterItem(
value: () => transactionFilterStore.displayAll,
caption: S.current.all_transactions,
onChanged: transactionFilterStore.toggleAll),
FilterItem(
value: () => transactionFilterStore.displayIncoming,
caption: S.current.incoming,
onChanged: (value) => transactionFilterStore.toggleIncoming()),
onChanged:transactionFilterStore.toggleIncoming),
FilterItem(
value: () => transactionFilterStore.displayOutgoing,
caption: S.current.outgoing,
onChanged: (value) => transactionFilterStore.toggleOutgoing()),
onChanged: transactionFilterStore.toggleOutgoing),
// FilterItem(
// value: () => false,
// caption: S.current.transactions_by_date,
// onChanged: null),
],
S.current.trades: [
FilterItem(
value: () => tradeFilterStore.displayAllTrades,
caption: S.current.all_trades,
onChanged: () => tradeFilterStore
.toggleDisplayExchange(ExchangeProviderDescription.all)),
FilterItem(
value: () => tradeFilterStore.displayChangeNow,
caption: 'Change.NOW',
onChanged: (value) => tradeFilterStore
caption: ExchangeProviderDescription.changeNow.title,
onChanged: () => tradeFilterStore
.toggleDisplayExchange(ExchangeProviderDescription.changeNow)),
FilterItem(
value: () => tradeFilterStore.displaySideShift,
caption: ExchangeProviderDescription.sideShift.title,
onChanged: () => tradeFilterStore
.toggleDisplayExchange(ExchangeProviderDescription.sideShift)),
FilterItem(
value: () => tradeFilterStore.displaySimpleSwap,
caption: ExchangeProviderDescription.simpleSwap.title,
onChanged: () => tradeFilterStore
.toggleDisplayExchange(ExchangeProviderDescription.simpleSwap)),
]
},
subname = '',
+3 -1
View File
@@ -1,3 +1,5 @@
import 'package:mobx/mobx.dart';
class FilterItem {
FilterItem({
required this.value,
@@ -6,5 +8,5 @@ class FilterItem {
bool Function() value;
String caption;
Function(bool) onChanged;
Function onChanged;
}
@@ -72,6 +72,18 @@ abstract class ExchangeTradeViewModelBase with Store {
? '\n\n' + S.current.xrp_extra_info
: '';
@computed
String get pendingTransactionFiatAmountValueFormatted =>
sendViewModel.isFiatDisabled
? '' : sendViewModel.pendingTransactionFiatAmount
+ ' ' + sendViewModel.fiat.title;
@computed
String get pendingTransactionFeeFiatAmountFormatted =>
sendViewModel.isFiatDisabled
? '' : sendViewModel.pendingTransactionFeeFiatAmount
+ ' ' + sendViewModel.fiat.title;
@observable
ObservableList<ExchangeTradeItem> items;
@@ -6,8 +6,8 @@ import 'package:cake_wallet/entities/preferences_key.dart';
import 'package:cake_wallet/exchange/sideshift/sideshift_exchange_provider.dart';
import 'package:cake_wallet/exchange/sideshift/sideshift_request.dart';
import 'package:cake_wallet/exchange/simpleswap/simpleswap_exchange_provider.dart';
import 'package:cake_wallet/view_model/settings/settings_view_model.dart';
import 'package:cake_wallet/exchange/simpleswap/simpleswap_request.dart';
import 'package:cw_core/transaction_priority.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/sync_status.dart';
@@ -42,9 +42,8 @@ class ExchangeViewModel = ExchangeViewModelBase with _$ExchangeViewModel;
abstract class ExchangeViewModelBase with Store {
ExchangeViewModelBase(this.wallet, this.trades, this._exchangeTemplateStore,
this.tradesStore, this._settingsStore, this.sharedPreferences, this._settingsViewModel)
this.tradesStore, this._settingsStore, this.sharedPreferences)
: _cryptoNumberFormat = NumberFormat(),
isReverse = false,
isFixedRateMode = false,
isReceiveAmountEntered = false,
depositAmount = '',
@@ -112,7 +111,11 @@ abstract class ExchangeViewModelBase with Store {
loadLimits();
reaction(
(_) => isFixedRateMode,
(Object _) => loadLimits());
(Object _) {
loadLimits();
_bestRate = 0;
_calculateBestRate();
});
}
final WalletBase wallet;
@@ -189,6 +192,19 @@ abstract class ExchangeViewModelBase with Store {
ObservableList<ExchangeTemplate> get templates =>
_exchangeTemplateStore.templates;
@computed
TransactionPriority get transactionPriority {
final priority = _settingsStore.priority[wallet.type];
if (priority == null) {
throw Exception('Unexpected type ${wallet.type.toString()}');
}
return priority;
}
bool get hasAllAmount =>
wallet.type == WalletType.bitcoin && depositCurrency == wallet.currency;
@@ -198,11 +214,11 @@ abstract class ExchangeViewModelBase with Store {
switch (wallet.type) {
case WalletType.monero:
case WalletType.haven:
return _settingsViewModel.transactionPriority == monero!.getMoneroTransactionPrioritySlow();
return transactionPriority == monero!.getMoneroTransactionPrioritySlow();
case WalletType.bitcoin:
return _settingsViewModel.transactionPriority == bitcoin!.getBitcoinTransactionPrioritySlow();
return transactionPriority == bitcoin!.getBitcoinTransactionPrioritySlow();
case WalletType.litecoin:
return _settingsViewModel.transactionPriority == bitcoin!.getLitecoinTransactionPrioritySlow();
return transactionPriority == bitcoin!.getLitecoinTransactionPrioritySlow();
default:
return false;
}
@@ -214,14 +230,10 @@ abstract class ExchangeViewModelBase with Store {
Limits limits;
bool isReverse;
NumberFormat _cryptoNumberFormat;
final SettingsStore _settingsStore;
final SettingsViewModel _settingsViewModel;
double _bestRate = 0.0;
late Timer bestRateSync;
@@ -247,7 +259,6 @@ abstract class ExchangeViewModelBase with Store {
@action
Future<void> changeReceiveAmount({required String amount}) async {
receiveAmount = amount;
isReverse = true;
if (amount.isEmpty) {
depositAmount = '';
@@ -272,7 +283,6 @@ abstract class ExchangeViewModelBase with Store {
@action
Future<void> changeDepositAmount({required String amount}) async {
depositAmount = amount;
isReverse = false;
if (amount.isEmpty) {
depositAmount = '';
@@ -296,14 +306,17 @@ abstract class ExchangeViewModelBase with Store {
}
Future<void> _calculateBestRate() async {
final amount = double.tryParse(isFixedRateMode ? receiveAmount : depositAmount) ?? 1;
final result = await Future.wait<double>(
_tradeAvailableProviders
.map((element) => element.calculateAmount(
.where((element) => !isFixedRateMode || element.supportsFixedRate)
.map((element) => element.fetchRate(
from: depositCurrency,
to: receiveCurrency,
amount: 1,
amount: amount,
isFixedRateMode: isFixedRateMode,
isReceiveAmount: false))
isReceiveAmount: isFixedRateMode))
);
_sortedAvailableProviders.clear();
@@ -334,7 +347,7 @@ abstract class ExchangeViewModelBase with Store {
? depositCurrency
: receiveCurrency;
double lowestMin = double.maxFinite;
double? lowestMin = double.maxFinite;
double? highestMax = 0.0;
for (var provider in selectedProviders) {
@@ -349,8 +362,8 @@ abstract class ExchangeViewModelBase with Store {
to: to,
isFixedRateMode: isFixedRateMode);
if (tempLimits.min != null && tempLimits.min! < lowestMin) {
lowestMin = tempLimits.min!;
if (lowestMin != null && (tempLimits.min ?? -1) < lowestMin) {
lowestMin = tempLimits.min;
}
if (highestMax != null && (tempLimits.max ?? double.maxFinite) > highestMax) {
highestMax = tempLimits.max;
@@ -360,7 +373,7 @@ abstract class ExchangeViewModelBase with Store {
}
}
if (lowestMin < double.maxFinite) {
if (lowestMin != double.maxFinite) {
limits = Limits(min: lowestMin, max: highestMax);
limitsState = LimitsLoadedSuccessfully(limits: limits);
@@ -388,7 +401,7 @@ abstract class ExchangeViewModelBase with Store {
settleAddress: receiveAddress,
refundAddress: depositAddress,
);
amount = depositAmount;
amount = isFixedRateMode ? receiveAmount : depositAmount;
}
if (provider is SimpleSwapExchangeProvider) {
@@ -399,7 +412,7 @@ abstract class ExchangeViewModelBase with Store {
address: receiveAddress,
refundAddress: depositAddress,
);
amount = depositAmount;
amount = isFixedRateMode ? receiveAmount : depositAmount;
}
if (provider is XMRTOExchangeProvider) {
@@ -411,7 +424,7 @@ abstract class ExchangeViewModelBase with Store {
address: receiveAddress,
refundAddress: depositAddress,
isBTCRequest: isReceiveAmountEntered);
amount = depositAmount;
amount = isFixedRateMode ? receiveAmount : depositAmount;
}
if (provider is ChangeNowExchangeProvider) {
@@ -422,8 +435,8 @@ abstract class ExchangeViewModelBase with Store {
toAmount: receiveAmount.replaceAll(',', '.'),
refundAddress: depositAddress,
address: receiveAddress,
isReverse: isReverse);
amount = isReverse ? receiveAmount : depositAmount;
isReverse: isFixedRateMode);
amount = isFixedRateMode ? receiveAmount : depositAmount;
}
if (provider is MorphTokenExchangeProvider) {
@@ -433,13 +446,13 @@ abstract class ExchangeViewModelBase with Store {
amount: depositAmount.replaceAll(',', '.'),
refundAddress: depositAddress,
address: receiveAddress);
amount = depositAmount;
amount = isFixedRateMode ? receiveAmount : depositAmount;
}
amount = amount.replaceAll(',', '.');
if (limitsState is LimitsLoadedSuccessfully) {
if (double.parse(amount) < limits.min!) {
if (limits.max != null && double.parse(amount) < limits.min!) {
continue;
} else if (limits.max != null && double.parse(amount) > limits.max!) {
continue;
@@ -1,29 +1,51 @@
import 'package:cake_wallet/core/execution_state.dart';
import 'package:cake_wallet/ionia/ionia_gift_card.dart';
import 'package:cake_wallet/ionia/ionia_service.dart';
import 'package:mobx/mobx.dart';
part 'ionia_custom_redeem_view_model.g.dart';
class IoniaCustomRedeemViewModel = IoniaCustomRedeemViewModelBase with _$IoniaCustomRedeemViewModel;
abstract class IoniaCustomRedeemViewModelBase with Store {
IoniaCustomRedeemViewModelBase(this.giftCard)
: amount = 0;
IoniaCustomRedeemViewModelBase({
required this.giftCard,
required this.ioniaService,
}) : amount = 0,
redeemState = InitialExecutionState();
final IoniaGiftCard giftCard;
final IoniaService ioniaService;
@observable
ExecutionState redeemState;
@observable
double amount;
@computed
double get remaining => amount <= giftCard.remainingAmount ? giftCard.remainingAmount - amount : 0;
double get remaining =>
amount <= giftCard.remainingAmount ? giftCard.remainingAmount - amount : 0;
@computed
String get formattedRemaining => remaining.toStringAsFixed(2);
String get formattedRemaining => remaining.toStringAsFixed(2);
@computed
bool get disableRedeem => amount > giftCard.remainingAmount;
@action
void updateAmount(String text){
amount = text.isEmpty ? 0 : (double.parse(text.replaceAll(',', '.')) ?? 0);
void updateAmount(String text) {
amount = double.tryParse(text.replaceAll(',', '.')) ?? 0;
}
}
@action
Future<void> addCustomRedeem() async {
try {
redeemState = IsExecutingState();
await ioniaService.redeem(giftCardId: giftCard.id, amount: amount);
redeemState = ExecutedSuccessfullyState();
} catch (e) {
redeemState = FailureState(e.toString());
}
}
}
@@ -6,21 +6,19 @@ import 'package:device_display_brightness/device_display_brightness.dart';
part 'ionia_gift_card_details_view_model.g.dart';
class IoniaGiftCardDetailsViewModel = IoniaGiftCardDetailsViewModelBase with _$IoniaGiftCardDetailsViewModel;
class IoniaGiftCardDetailsViewModel = IoniaGiftCardDetailsViewModelBase
with _$IoniaGiftCardDetailsViewModel;
abstract class IoniaGiftCardDetailsViewModelBase with Store {
IoniaGiftCardDetailsViewModelBase({
required this.ioniaService,
required this.giftCard})
: redeemState = InitialExecutionState(),
remainingAmount = giftCard.remainingAmount,
brightness = 0;
IoniaGiftCardDetailsViewModelBase({required this.ioniaService, required this.giftCard})
: redeemState = InitialExecutionState(),
remainingAmount = giftCard.remainingAmount,
brightness = 0;
final IoniaService ioniaService;
double brightness;
@observable
IoniaGiftCard giftCard;
@@ -35,21 +33,22 @@ abstract class IoniaGiftCardDetailsViewModelBase with Store {
giftCard.remainingAmount = remainingAmount;
try {
redeemState = IsExecutingState();
await ioniaService.redeem(giftCard);
await ioniaService.redeem(giftCardId: giftCard.id, amount: giftCard.remainingAmount);
giftCard = await ioniaService.getGiftCard(id: giftCard.id);
redeemState = ExecutedSuccessfullyState();
} catch(e) {
} catch (e) {
redeemState = FailureState(e.toString());
}
}
@action
void updateRemaining(double amount){
remainingAmount = amount;
Future<void> refeshCard() async {
giftCard = await ioniaService.getGiftCard(id: giftCard.id);
remainingAmount = giftCard.remainingAmount;
}
void increaseBrightness() async {
brightness = await DeviceDisplayBrightness.getBrightness();
await DeviceDisplayBrightness.setBrightness(1.0);
}
}
}

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