mirror of
https://github.com/Such-Software/hash-wallet.git
synced 2026-10-19 05:58:50 +02:00
Merge branch 'main' of https://github.com/cake-tech/cake_wallet into CW-237-enhance-error-catching-reporting
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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 =
|
||||
|
||||
@@ -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 '';
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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> {};
|
||||
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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';
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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(
|
||||
|
||||
@@ -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}'));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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];
|
||||
|
||||
|
||||
@@ -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);
|
||||
},
|
||||
);
|
||||
}),
|
||||
]),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,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',
|
||||
|
||||
@@ -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,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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!),
|
||||
))
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
+2
-2
@@ -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(
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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) ||
|
||||
|
||||
@@ -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
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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 = '',
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user