mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2025-01-23 19:16:09 +00:00
Merge branch '4.0.8' of github.com:cake-tech/cake_wallet_private into btc
This commit is contained in:
commit
c5b739d36a
32 changed files with 364 additions and 195 deletions
|
@ -1,5 +1,5 @@
|
||||||
# Cake Wallet
|
# Cake Wallet
|
||||||
|
|
||||||
The project description, motivation, build scripts, instructions, tests will be added soon (Spring 2020);
|
The project description, motivation, build scripts, instructions, tests will be added soon (Spring 202X);
|
||||||
|
|
||||||
Copyright (c) 2020 Cake Technologies LLC.
|
Copyright (c) 2020 Cake Technologies LLC.
|
||||||
|
|
|
@ -6,6 +6,8 @@
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:label="Cake Wallet"
|
android:label="Cake Wallet"
|
||||||
|
android:allowBackup="false"
|
||||||
|
android:fullBackupContent="false"
|
||||||
android:icon="@mipmap/ic_launcher">
|
android:icon="@mipmap/ic_launcher">
|
||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
|
|
|
@ -1,6 +1,15 @@
|
||||||
package com.cakewallet.cake_wallet;
|
package com.cakewallet.cake_wallet;
|
||||||
|
|
||||||
import io.flutter.embedding.android.FlutterActivity;
|
import androidx.annotation.NonNull;
|
||||||
|
import io.flutter.embedding.android.FlutterFragmentActivity;
|
||||||
|
import io.flutter.embedding.engine.FlutterEngine;
|
||||||
|
import io.flutter.plugins.GeneratedPluginRegistrant;
|
||||||
|
|
||||||
public class MainActivity extends FlutterActivity {
|
|
||||||
}
|
public class MainActivity extends FlutterFragmentActivity {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
|
||||||
|
GeneratedPluginRegistrant.registerWith(flutterEngine);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,13 @@
|
||||||
package com.cakewallet.cake_wallet
|
package com.cakewallet.cake_wallet
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
import io.flutter.embedding.android.FlutterActivity
|
import io.flutter.embedding.android.FlutterActivity
|
||||||
|
import io.flutter.embedding.android.FlutterFragmentActivity
|
||||||
|
import io.flutter.embedding.engine.FlutterEngine
|
||||||
|
import io.flutter.plugins.GeneratedPluginRegistrant
|
||||||
|
|
||||||
class MainActivity: FlutterActivity() {
|
class MainActivity: FlutterActivity() {
|
||||||
|
override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine){
|
||||||
|
GeneratedPluginRegistrant.registerWith(flutterEngine);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -371,7 +371,7 @@
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"$(PROJECT_DIR)/Flutter",
|
"$(PROJECT_DIR)/Flutter",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 4.0.3;
|
MARKETING_VERSION = 4.0.8;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.fotolockr.cakewallet;
|
PRODUCT_BUNDLE_IDENTIFIER = com.fotolockr.cakewallet;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||||
|
@ -511,7 +511,7 @@
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"$(PROJECT_DIR)/Flutter",
|
"$(PROJECT_DIR)/Flutter",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 4.0.3;
|
MARKETING_VERSION = 4.0.8;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.fotolockr.cakewallet;
|
PRODUCT_BUNDLE_IDENTIFIER = com.fotolockr.cakewallet;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||||
|
@ -545,7 +545,7 @@
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"$(PROJECT_DIR)/Flutter",
|
"$(PROJECT_DIR)/Flutter",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 4.0.3;
|
MARKETING_VERSION = 4.0.8;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.fotolockr.cakewallet;
|
PRODUCT_BUNDLE_IDENTIFIER = com.fotolockr.cakewallet;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||||
|
|
|
@ -2,12 +2,14 @@ import 'package:cake_wallet/bitcoin/bitcoin_wallet_service.dart';
|
||||||
import 'package:cake_wallet/core/wallet_service.dart';
|
import 'package:cake_wallet/core/wallet_service.dart';
|
||||||
import 'package:cake_wallet/entities/biometric_auth.dart';
|
import 'package:cake_wallet/entities/biometric_auth.dart';
|
||||||
import 'package:cake_wallet/entities/contact_record.dart';
|
import 'package:cake_wallet/entities/contact_record.dart';
|
||||||
|
import 'package:cake_wallet/entities/load_current_wallet.dart';
|
||||||
import 'package:cake_wallet/entities/transaction_description.dart';
|
import 'package:cake_wallet/entities/transaction_description.dart';
|
||||||
import 'package:cake_wallet/entities/transaction_info.dart';
|
import 'package:cake_wallet/entities/transaction_info.dart';
|
||||||
import 'package:cake_wallet/monero/monero_wallet_service.dart';
|
import 'package:cake_wallet/monero/monero_wallet_service.dart';
|
||||||
import 'package:cake_wallet/entities/contact.dart';
|
import 'package:cake_wallet/entities/contact.dart';
|
||||||
import 'package:cake_wallet/entities/node.dart';
|
import 'package:cake_wallet/entities/node.dart';
|
||||||
import 'package:cake_wallet/exchange/trade.dart';
|
import 'package:cake_wallet/exchange/trade.dart';
|
||||||
|
import 'package:cake_wallet/reactions/on_authentication_state_change.dart';
|
||||||
|
|
||||||
import 'package:cake_wallet/src/screens/contact/contact_list_page.dart';
|
import 'package:cake_wallet/src/screens/contact/contact_list_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/contact/contact_page.dart';
|
import 'package:cake_wallet/src/screens/contact/contact_page.dart';
|
||||||
|
@ -200,6 +202,12 @@ Future setup(
|
||||||
}
|
}
|
||||||
|
|
||||||
authPageState.changeProcessText('Loading the wallet');
|
authPageState.changeProcessText('Loading the wallet');
|
||||||
|
|
||||||
|
if (loginError != null) {
|
||||||
|
authPageState
|
||||||
|
.changeProcessText('ERROR: ${loginError.toString()}');
|
||||||
|
}
|
||||||
|
|
||||||
ReactionDisposer _reaction;
|
ReactionDisposer _reaction;
|
||||||
_reaction = reaction((_) => appStore.wallet, (Object _) {
|
_reaction = reaction((_) => appStore.wallet, (Object _) {
|
||||||
_reaction?.reaction?.dispose();
|
_reaction?.reaction?.dispose();
|
||||||
|
|
|
@ -19,3 +19,10 @@ Future<String> pathForWalletDir({@required String name, @required WalletType ty
|
||||||
Future<String> pathForWallet({@required String name, @required WalletType type}) async =>
|
Future<String> pathForWallet({@required String name, @required WalletType type}) async =>
|
||||||
await pathForWalletDir(name: name, type: type)
|
await pathForWalletDir(name: name, type: type)
|
||||||
.then((path) => path + '/$name');
|
.then((path) => path + '/$name');
|
||||||
|
|
||||||
|
Future<String> outdatedAndroidPathForWalletDir({String name}) async {
|
||||||
|
final directory = await getApplicationDocumentsDirectory();
|
||||||
|
final pathDir = directory.path + '/$name';
|
||||||
|
|
||||||
|
return pathDir;
|
||||||
|
}
|
|
@ -82,37 +82,32 @@ final dates = {
|
||||||
"2020-8": 2153983,
|
"2020-8": 2153983,
|
||||||
"2020-9": 2176466,
|
"2020-9": 2176466,
|
||||||
"2020-10": 2198453,
|
"2020-10": 2198453,
|
||||||
"2020-11": 2221803
|
"2020-11": 2220000
|
||||||
};
|
};
|
||||||
|
|
||||||
|
final heightCoefficient = 0.7;
|
||||||
|
|
||||||
int getHeigthByDate({DateTime date}) {
|
int getHeigthByDate({DateTime date}) {
|
||||||
final raw = '${date.year}' + '-' + '${date.month}';
|
final raw = '${date.year}' + '-' + '${date.month}';
|
||||||
var endHeight = dates[raw] ?? 0;
|
final lastHeight = dates.values.last;
|
||||||
int preLastYear = date.year;
|
int startHeight;
|
||||||
int preLastMonth = date.month - 1;
|
int endHeight;
|
||||||
|
int height;
|
||||||
|
|
||||||
if (endHeight <= 0) {
|
if ((dates[raw] == null)||(dates[raw] == lastHeight)) {
|
||||||
|
startHeight = dates.values.toList()[dates.length - 2];
|
||||||
endHeight = dates.values.toList()[dates.length - 1];
|
endHeight = dates.values.toList()[dates.length - 1];
|
||||||
final preLastDate =
|
final heightPerDay = (endHeight - startHeight) / 31;
|
||||||
dateFormat.parse(dates.keys.elementAt(dates.keys.length - 2));
|
final daysHeight = (heightCoefficient * (date.day - 1) * heightPerDay).round();
|
||||||
preLastYear = preLastDate.year;
|
height = endHeight + daysHeight;
|
||||||
preLastMonth = preLastDate.month;
|
|
||||||
} else {
|
} else {
|
||||||
preLastYear = date.year;
|
startHeight = dates[raw];
|
||||||
preLastMonth = date.month - 1;
|
final index = dates.values.toList().indexOf(startHeight);
|
||||||
|
endHeight = dates.values.toList()[index + 1];
|
||||||
|
final heightPerDay = ((endHeight - startHeight) / 31).round();
|
||||||
|
final daysHeight = (date.day - 1) * heightPerDay;
|
||||||
|
height = startHeight + daysHeight - heightPerDay;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (preLastMonth <= 0) {
|
|
||||||
preLastMonth = 12;
|
|
||||||
preLastYear -= 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
final startRaw = '$preLastYear' + '-' + '$preLastMonth';
|
|
||||||
final startHeight = dates[startRaw];
|
|
||||||
final diff = endHeight - startHeight;
|
|
||||||
final heightPerDay = diff / 30;
|
|
||||||
final daysHeight = date.day * heightPerDay.round();
|
|
||||||
final height = endHeight + daysHeight;
|
|
||||||
|
|
||||||
return height;
|
return height;
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ abstract class MoneroAccountListBase with Store {
|
||||||
bool _isRefreshing;
|
bool _isRefreshing;
|
||||||
bool _isUpdating;
|
bool _isUpdating;
|
||||||
|
|
||||||
Future update() async {
|
void update() async {
|
||||||
if (_isUpdating) {
|
if (_isUpdating) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -121,8 +121,8 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance> with Store {
|
||||||
_onAccountChangeReaction?.reaction?.dispose();
|
_onAccountChangeReaction?.reaction?.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> validate() async {
|
bool validate() {
|
||||||
await accountList.update();
|
accountList.update();
|
||||||
final accountListLength = accountList.accounts?.length ?? 0;
|
final accountListLength = accountList.accounts?.length ?? 0;
|
||||||
|
|
||||||
if (accountListLength <= 0) {
|
if (accountListLength <= 0) {
|
||||||
|
|
|
@ -27,7 +27,7 @@ class MoneroRestoreWalletFromSeedCredentials extends WalletCredentials {
|
||||||
|
|
||||||
class MoneroWalletLoadingException implements Exception {
|
class MoneroWalletLoadingException implements Exception {
|
||||||
@override
|
@override
|
||||||
String toString() => 'The wallet is damaged.';
|
String toString() => 'Failure to load the wallet.';
|
||||||
}
|
}
|
||||||
|
|
||||||
class MoneroRestoreWalletFromKeysCredentials extends WalletCredentials {
|
class MoneroRestoreWalletFromKeysCredentials extends WalletCredentials {
|
||||||
|
@ -93,6 +93,11 @@ class MoneroWalletService extends WalletService<
|
||||||
Future<MoneroWallet> openWallet(String name, String password) async {
|
Future<MoneroWallet> openWallet(String name, String password) async {
|
||||||
try {
|
try {
|
||||||
final path = await pathForWallet(name: name, type: WalletType.monero);
|
final path = await pathForWallet(name: name, type: WalletType.monero);
|
||||||
|
|
||||||
|
if (!File(path).existsSync()) {
|
||||||
|
await repairOldAndroidWallet(name);
|
||||||
|
}
|
||||||
|
|
||||||
await monero_wallet_manager
|
await monero_wallet_manager
|
||||||
.openWalletAsync({'path': path, 'password': password});
|
.openWalletAsync({'path': path, 'password': password});
|
||||||
final walletInfo = walletInfoSource.values.firstWhere(
|
final walletInfo = walletInfoSource.values.firstWhere(
|
||||||
|
@ -100,18 +105,18 @@ class MoneroWalletService extends WalletService<
|
||||||
orElse: () => null);
|
orElse: () => null);
|
||||||
final wallet = MoneroWallet(
|
final wallet = MoneroWallet(
|
||||||
filename: monero_wallet.getFilename(), walletInfo: walletInfo);
|
filename: monero_wallet.getFilename(), walletInfo: walletInfo);
|
||||||
final isValid = await wallet.validate();
|
final isValid = wallet.validate();
|
||||||
|
|
||||||
if (!isValid) {
|
if (!isValid) {
|
||||||
if (wallet.seed?.isNotEmpty ?? false) {
|
// if (wallet.seed?.isNotEmpty ?? false) {
|
||||||
// let restore from seed in this case;
|
// let restore from seed in this case;
|
||||||
final seed = wallet.seed;
|
// final seed = wallet.seed;
|
||||||
final credentials = MoneroRestoreWalletFromSeedCredentials(
|
// final credentials = MoneroRestoreWalletFromSeedCredentials(
|
||||||
name: name, password: password, mnemonic: seed, height: 2000000)
|
// name: name, password: password, mnemonic: seed, height: 2000000)
|
||||||
..walletInfo = walletInfo;
|
// ..walletInfo = walletInfo;
|
||||||
await remove(name);
|
// await remove(name);
|
||||||
return restoreFromSeed(credentials);
|
// return restoreFromSeed(credentials);
|
||||||
}
|
// }
|
||||||
|
|
||||||
throw MoneroWalletLoadingException();
|
throw MoneroWalletLoadingException();
|
||||||
}
|
}
|
||||||
|
@ -187,4 +192,38 @@ class MoneroWalletService extends WalletService<
|
||||||
rethrow;
|
rethrow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> repairOldAndroidWallet(String name) async {
|
||||||
|
try {
|
||||||
|
if (!Platform.isAndroid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final oldAndroidWalletDirPath =
|
||||||
|
await outdatedAndroidPathForWalletDir(name: name);
|
||||||
|
final dir = Directory(oldAndroidWalletDirPath);
|
||||||
|
|
||||||
|
if (!dir.existsSync()) {
|
||||||
|
throw MoneroWalletLoadingException();
|
||||||
|
}
|
||||||
|
|
||||||
|
final newWalletDirPath =
|
||||||
|
await pathForWalletDir(name: name, type: WalletType.monero);
|
||||||
|
|
||||||
|
dir.listSync().forEach((f) {
|
||||||
|
final file = File(f.path);
|
||||||
|
final name = f.path.split('/').last;
|
||||||
|
final newPath = newWalletDirPath + '/$name';
|
||||||
|
final newFile = File(newPath);
|
||||||
|
print(file.path);
|
||||||
|
if (!newFile.existsSync()) {
|
||||||
|
newFile.createSync();
|
||||||
|
}
|
||||||
|
newFile.writeAsBytesSync(file.readAsBytesSync());
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
print(e.toString());
|
||||||
|
throw MoneroWalletLoadingException();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,14 @@ import 'package:cake_wallet/entities/crypto_currency.dart';
|
||||||
import 'package:cake_wallet/core/amount_converter.dart';
|
import 'package:cake_wallet/core/amount_converter.dart';
|
||||||
import 'package:cake_wallet/core/pending_transaction.dart';
|
import 'package:cake_wallet/core/pending_transaction.dart';
|
||||||
|
|
||||||
|
class DoubleSpendException implements Exception {
|
||||||
|
DoubleSpendException();
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() =>
|
||||||
|
'This transaction cannot be committed. This can be due to many reasons including the wallet not being synced, there is not enough XMR in your available balance, or previous transactions are not yet fully processed.';
|
||||||
|
}
|
||||||
|
|
||||||
class PendingMoneroTransaction with PendingTransaction {
|
class PendingMoneroTransaction with PendingTransaction {
|
||||||
PendingMoneroTransaction(this.pendingTransactionDescription);
|
PendingMoneroTransaction(this.pendingTransactionDescription);
|
||||||
|
|
||||||
|
@ -22,7 +30,18 @@ class PendingMoneroTransaction with PendingTransaction {
|
||||||
CryptoCurrency.xmr, pendingTransactionDescription.fee);
|
CryptoCurrency.xmr, pendingTransactionDescription.fee);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> commit() async =>
|
Future<void> commit() async {
|
||||||
|
try {
|
||||||
monero_transaction_history.commitTransactionFromPointerAddress(
|
monero_transaction_history.commitTransactionFromPointerAddress(
|
||||||
address: pendingTransactionDescription.pointerAddress);
|
address: pendingTransactionDescription.pointerAddress);
|
||||||
|
} catch (e) {
|
||||||
|
final message = e.toString();
|
||||||
|
|
||||||
|
if (message.contains('Reason: double spend')) {
|
||||||
|
throw DoubleSpendException();
|
||||||
|
}
|
||||||
|
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,13 +7,19 @@ import 'package:cake_wallet/store/authentication_store.dart';
|
||||||
|
|
||||||
ReactionDisposer _onAuthenticationStateChange;
|
ReactionDisposer _onAuthenticationStateChange;
|
||||||
|
|
||||||
|
dynamic loginError;
|
||||||
|
|
||||||
void startAuthenticationStateChange(AuthenticationStore authenticationStore,
|
void startAuthenticationStateChange(AuthenticationStore authenticationStore,
|
||||||
@required GlobalKey<NavigatorState> navigatorKey) {
|
@required GlobalKey<NavigatorState> navigatorKey) {
|
||||||
_onAuthenticationStateChange ??= autorun((_) async {
|
_onAuthenticationStateChange ??= autorun((_) async {
|
||||||
final state = authenticationStore.state;
|
final state = authenticationStore.state;
|
||||||
|
|
||||||
if (state == AuthenticationState.installed) {
|
if (state == AuthenticationState.installed) {
|
||||||
await loadCurrentWallet();
|
try {
|
||||||
|
await loadCurrentWallet();
|
||||||
|
} catch(e) {
|
||||||
|
loginError = e;
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,6 @@ void startCurrentFiatChangeReaction(AppStore appStore, SettingsStore settingsSto
|
||||||
(_) => settingsStore.fiatCurrency, (FiatCurrency fiatCurrency) async {
|
(_) => settingsStore.fiatCurrency, (FiatCurrency fiatCurrency) async {
|
||||||
final cryptoCurrency = appStore.wallet.currency;
|
final cryptoCurrency = appStore.wallet.currency;
|
||||||
fiatConversionStore.price = await FiatConversionService.fetchPrice(
|
fiatConversionStore.price = await FiatConversionService.fetchPrice(
|
||||||
cryptoCurrency, settingsStore.fiatCurrency);
|
cryptoCurrency, fiatCurrency);
|
||||||
});
|
});
|
||||||
}
|
}
|
|
@ -40,13 +40,14 @@ class AuthPageState extends State<AuthPage> {
|
||||||
reaction((_) => widget.authViewModel.state, (ExecutionState state) {
|
reaction((_) => widget.authViewModel.state, (ExecutionState state) {
|
||||||
if (state is ExecutedSuccessfullyState) {
|
if (state is ExecutedSuccessfullyState) {
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
_authBar?.dismiss();
|
||||||
if (widget.onAuthenticationFinished != null) {
|
if (widget.onAuthenticationFinished != null) {
|
||||||
widget.onAuthenticationFinished(true, this);
|
widget.onAuthenticationFinished(true, this);
|
||||||
} else {
|
} else {
|
||||||
_authBar?.dismiss();
|
|
||||||
showBar<void>(context, S.of(context).authenticated);
|
showBar<void>(context, S.of(context).authenticated);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
setState(() {});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state is IsExecutingState) {
|
if (state is IsExecutingState) {
|
||||||
|
|
|
@ -48,14 +48,15 @@ abstract class BasePage extends StatelessWidget {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
final _backButton = Image.asset('assets/images/back_arrow.png',
|
final _backButton = Icon(Icons.arrow_back_ios,
|
||||||
color: titleColor ?? Theme.of(context).primaryTextTheme.title.color);
|
color: titleColor ?? Theme.of(context).primaryTextTheme.title.color,
|
||||||
|
size: 16,);
|
||||||
final _closeButton =
|
final _closeButton =
|
||||||
isDarkTheme ? _closeButtonImageDarkTheme : _closeButtonImage;
|
isDarkTheme ? _closeButtonImageDarkTheme : _closeButtonImage;
|
||||||
|
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
height: 37,
|
height: 37,
|
||||||
width: isModalBackButton ? 37 : 20,
|
width: 37,
|
||||||
child: ButtonTheme(
|
child: ButtonTheme(
|
||||||
minWidth: double.minPositive,
|
minWidth: double.minPositive,
|
||||||
child: FlatButton(
|
child: FlatButton(
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import 'package:cake_wallet/src/widgets/keyboard_done_button.dart';
|
||||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
|
@ -15,9 +16,10 @@ import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_h
|
||||||
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_item.dart';
|
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_item.dart';
|
||||||
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_view_model.dart';
|
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_view_model.dart';
|
||||||
import 'package:cake_wallet/src/screens/receive/widgets/qr_widget.dart';
|
import 'package:cake_wallet/src/screens/receive/widgets/qr_widget.dart';
|
||||||
|
import 'package:keyboard_actions/keyboard_actions.dart';
|
||||||
|
|
||||||
class ReceivePage extends BasePage {
|
class ReceivePage extends BasePage {
|
||||||
ReceivePage({this.addressListViewModel});
|
ReceivePage({this.addressListViewModel}) : _cryptoAmountFocus = FocusNode();
|
||||||
|
|
||||||
final WalletAddressListViewModel addressListViewModel;
|
final WalletAddressListViewModel addressListViewModel;
|
||||||
|
|
||||||
|
@ -33,6 +35,8 @@ class ReceivePage extends BasePage {
|
||||||
@override
|
@override
|
||||||
Color get titleColor => Colors.white;
|
Color get titleColor => Colors.white;
|
||||||
|
|
||||||
|
final FocusNode _cryptoAmountFocus;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget Function(BuildContext, Widget) get rootWrapper =>
|
Widget Function(BuildContext, Widget) get rootWrapper =>
|
||||||
(BuildContext context, Widget scaffold) => Container(
|
(BuildContext context, Widget scaffold) => Container(
|
||||||
|
@ -67,93 +71,109 @@ class ReceivePage extends BasePage {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget body(BuildContext context) {
|
Widget body(BuildContext context) {
|
||||||
return SingleChildScrollView(
|
return KeyboardActions(
|
||||||
child: Column(
|
config: KeyboardActionsConfig(
|
||||||
children: <Widget>[
|
keyboardActionsPlatform: KeyboardActionsPlatform.IOS,
|
||||||
Padding(
|
keyboardBarColor: isDarkTheme
|
||||||
padding: EdgeInsets.fromLTRB(24, 80, 24, 40),
|
? Color.fromRGBO(48, 51, 60, 1.0)
|
||||||
child: QRWidget(
|
: Color.fromRGBO(98, 98, 98, 1.0),
|
||||||
addressListViewModel: addressListViewModel,
|
nextFocus: false,
|
||||||
isAmountFieldShow: true,
|
actions: [
|
||||||
),
|
KeyboardActionsItem(
|
||||||
|
focusNode: _cryptoAmountFocus,
|
||||||
|
toolbarButtons: [(_) => KeyboardDoneButton()],
|
||||||
|
)
|
||||||
|
]),
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
child: Column(
|
||||||
|
children: <Widget>[
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.fromLTRB(24, 80, 24, 40),
|
||||||
|
child: QRWidget(
|
||||||
|
addressListViewModel: addressListViewModel,
|
||||||
|
isAmountFieldShow: true,
|
||||||
|
amountTextFieldFocusNode: _cryptoAmountFocus),
|
||||||
|
),
|
||||||
|
Observer(
|
||||||
|
builder: (_) => ListView.separated(
|
||||||
|
padding: EdgeInsets.all(0),
|
||||||
|
separatorBuilder: (context, _) => Container(
|
||||||
|
height: 1, color: Theme.of(context).dividerColor),
|
||||||
|
shrinkWrap: true,
|
||||||
|
physics: NeverScrollableScrollPhysics(),
|
||||||
|
itemCount: addressListViewModel.items.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final item = addressListViewModel.items[index];
|
||||||
|
Widget cell = Container();
|
||||||
|
|
||||||
|
if (item is WalletAccountListHeader) {
|
||||||
|
cell = HeaderTile(
|
||||||
|
onTap: () async => await showPopUp<void>(
|
||||||
|
context: context,
|
||||||
|
builder: (_) =>
|
||||||
|
getIt.get<MoneroAccountListPage>()),
|
||||||
|
title: S.of(context).accounts,
|
||||||
|
icon: Icon(
|
||||||
|
Icons.arrow_forward_ios,
|
||||||
|
size: 14,
|
||||||
|
color:
|
||||||
|
Theme.of(context).textTheme.display1.color,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item is WalletAddressListHeader) {
|
||||||
|
cell = HeaderTile(
|
||||||
|
onTap: () => Navigator.of(context)
|
||||||
|
.pushNamed(Routes.newSubaddress),
|
||||||
|
title: S.of(context).addresses,
|
||||||
|
icon: Icon(
|
||||||
|
Icons.add,
|
||||||
|
size: 20,
|
||||||
|
color:
|
||||||
|
Theme.of(context).textTheme.display1.color,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item is WalletAddressListItem) {
|
||||||
|
cell = Observer(builder: (_) {
|
||||||
|
final isCurrent = item.address ==
|
||||||
|
addressListViewModel.address.address;
|
||||||
|
final backgroundColor = isCurrent
|
||||||
|
? Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.display3
|
||||||
|
.decorationColor
|
||||||
|
: Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.display2
|
||||||
|
.decorationColor;
|
||||||
|
final textColor = isCurrent
|
||||||
|
? Theme.of(context).textTheme.display3.color
|
||||||
|
: Theme.of(context).textTheme.display2.color;
|
||||||
|
|
||||||
|
return AddressCell.fromItem(item,
|
||||||
|
isCurrent: isCurrent,
|
||||||
|
backgroundColor: backgroundColor,
|
||||||
|
textColor: textColor,
|
||||||
|
onTap: (_) =>
|
||||||
|
addressListViewModel.setAddress(item),
|
||||||
|
onEdit: () => Navigator.of(context).pushNamed(
|
||||||
|
Routes.newSubaddress,
|
||||||
|
arguments: item));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return index != 0
|
||||||
|
? cell
|
||||||
|
: ClipRRect(
|
||||||
|
borderRadius: BorderRadius.only(
|
||||||
|
topLeft: Radius.circular(30),
|
||||||
|
topRight: Radius.circular(30)),
|
||||||
|
child: cell,
|
||||||
|
);
|
||||||
|
})),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
Observer(
|
));
|
||||||
builder: (_) => ListView.separated(
|
|
||||||
padding: EdgeInsets.all(0),
|
|
||||||
separatorBuilder: (context, _) => Container(
|
|
||||||
height: 1, color: Theme.of(context).dividerColor),
|
|
||||||
shrinkWrap: true,
|
|
||||||
physics: NeverScrollableScrollPhysics(),
|
|
||||||
itemCount: addressListViewModel.items.length,
|
|
||||||
itemBuilder: (context, index) {
|
|
||||||
final item = addressListViewModel.items[index];
|
|
||||||
Widget cell = Container();
|
|
||||||
|
|
||||||
if (item is WalletAccountListHeader) {
|
|
||||||
cell = HeaderTile(
|
|
||||||
onTap: () async => await showPopUp<void>(
|
|
||||||
context: context,
|
|
||||||
builder: (_) =>
|
|
||||||
getIt.get<MoneroAccountListPage>()),
|
|
||||||
title: S.of(context).accounts,
|
|
||||||
icon: Icon(
|
|
||||||
Icons.arrow_forward_ios,
|
|
||||||
size: 14,
|
|
||||||
color: Theme.of(context).textTheme.display1.color,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item is WalletAddressListHeader) {
|
|
||||||
cell = HeaderTile(
|
|
||||||
onTap: () => Navigator.of(context)
|
|
||||||
.pushNamed(Routes.newSubaddress),
|
|
||||||
title: S.of(context).addresses,
|
|
||||||
icon: Icon(
|
|
||||||
Icons.add,
|
|
||||||
size: 20,
|
|
||||||
color: Theme.of(context).textTheme.display1.color,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item is WalletAddressListItem) {
|
|
||||||
cell = Observer(builder: (_) {
|
|
||||||
final isCurrent = item.address ==
|
|
||||||
addressListViewModel.address.address;
|
|
||||||
final backgroundColor = isCurrent
|
|
||||||
? Theme.of(context)
|
|
||||||
.textTheme
|
|
||||||
.display3
|
|
||||||
.decorationColor
|
|
||||||
: Theme.of(context)
|
|
||||||
.textTheme
|
|
||||||
.display2
|
|
||||||
.decorationColor;
|
|
||||||
final textColor = isCurrent
|
|
||||||
? Theme.of(context).textTheme.display3.color
|
|
||||||
: Theme.of(context).textTheme.display2.color;
|
|
||||||
|
|
||||||
return AddressCell.fromItem(item,
|
|
||||||
isCurrent: isCurrent,
|
|
||||||
backgroundColor: backgroundColor,
|
|
||||||
textColor: textColor,
|
|
||||||
onTap: (_) => addressListViewModel.setAddress(item),
|
|
||||||
onEdit: () => Navigator.of(context).pushNamed(
|
|
||||||
Routes.newSubaddress,
|
|
||||||
arguments: item));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return index != 0
|
|
||||||
? cell
|
|
||||||
: ClipRRect(
|
|
||||||
borderRadius: BorderRadius.only(
|
|
||||||
topLeft: Radius.circular(30),
|
|
||||||
topRight: Radius.circular(30)),
|
|
||||||
child: cell,
|
|
||||||
);
|
|
||||||
})),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,9 @@ import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_v
|
||||||
|
|
||||||
class QRWidget extends StatelessWidget {
|
class QRWidget extends StatelessWidget {
|
||||||
QRWidget(
|
QRWidget(
|
||||||
{@required this.addressListViewModel, this.isAmountFieldShow = false})
|
{@required this.addressListViewModel,
|
||||||
|
this.isAmountFieldShow = false,
|
||||||
|
this.amountTextFieldFocusNode})
|
||||||
: amountController = TextEditingController(),
|
: amountController = TextEditingController(),
|
||||||
_formKey = GlobalKey<FormState>() {
|
_formKey = GlobalKey<FormState>() {
|
||||||
amountController.addListener(() => addressListViewModel.amount =
|
amountController.addListener(() => addressListViewModel.amount =
|
||||||
|
@ -21,6 +23,7 @@ class QRWidget extends StatelessWidget {
|
||||||
final WalletAddressListViewModel addressListViewModel;
|
final WalletAddressListViewModel addressListViewModel;
|
||||||
final bool isAmountFieldShow;
|
final bool isAmountFieldShow;
|
||||||
final TextEditingController amountController;
|
final TextEditingController amountController;
|
||||||
|
final FocusNode amountTextFieldFocusNode;
|
||||||
final GlobalKey<FormState> _formKey;
|
final GlobalKey<FormState> _formKey;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -45,7 +48,7 @@ class QRWidget extends StatelessWidget {
|
||||||
data: addressListViewModel.uri.toString(),
|
data: addressListViewModel.uri.toString(),
|
||||||
backgroundColor: Colors.transparent,
|
backgroundColor: Colors.transparent,
|
||||||
foregroundColor: Colors.white,
|
foregroundColor: Colors.white,
|
||||||
//Theme.of(context).textTheme.headline.color,
|
//Theme.of(context).textTheme.headline.color,
|
||||||
))))),
|
))))),
|
||||||
Spacer(flex: 3)
|
Spacer(flex: 3)
|
||||||
]),
|
]),
|
||||||
|
@ -68,6 +71,7 @@ class QRWidget extends StatelessWidget {
|
||||||
child: Form(
|
child: Form(
|
||||||
key: _formKey,
|
key: _formKey,
|
||||||
child: BaseTextFormField(
|
child: BaseTextFormField(
|
||||||
|
focusNode: amountTextFieldFocusNode,
|
||||||
controller: amountController,
|
controller: amountController,
|
||||||
keyboardType: TextInputType.numberWithOptions(
|
keyboardType: TextInputType.numberWithOptions(
|
||||||
decimal: true),
|
decimal: true),
|
||||||
|
|
|
@ -21,23 +21,9 @@ class RescanPage extends BasePage {
|
||||||
padding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
|
padding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
|
||||||
child:
|
child:
|
||||||
Column(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
|
Column(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
|
||||||
Column(
|
BlockchainHeightWidget(key: _blockchainHeightWidgetKey,
|
||||||
children: <Widget>[
|
onHeightOrDateEntered: (value) =>
|
||||||
BlockchainHeightWidget(key: _blockchainHeightWidgetKey),
|
_rescanViewModel.isButtonEnabled = value),
|
||||||
Padding(
|
|
||||||
padding: EdgeInsets.only(left: 40, right: 40, top: 24),
|
|
||||||
child: Text(
|
|
||||||
S.of(context).restore_from_date_or_blockheight,
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
fontWeight: FontWeight.normal,
|
|
||||||
color: Theme.of(context).hintColor
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
Observer(
|
Observer(
|
||||||
builder: (_) => LoadingPrimaryButton(
|
builder: (_) => LoadingPrimaryButton(
|
||||||
isLoading:
|
isLoading:
|
||||||
|
@ -51,6 +37,7 @@ class RescanPage extends BasePage {
|
||||||
},
|
},
|
||||||
color: Theme.of(context).accentTextTheme.body2.color,
|
color: Theme.of(context).accentTextTheme.body2.color,
|
||||||
textColor: Colors.white,
|
textColor: Colors.white,
|
||||||
|
isDisabled: !_rescanViewModel.isButtonEnabled,
|
||||||
))
|
))
|
||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
|
|
|
@ -6,7 +6,10 @@ import 'package:cake_wallet/src/widgets/blockchain_height_widget.dart';
|
||||||
import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
|
import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
|
||||||
|
|
||||||
class WalletRestoreFromKeysFrom extends StatefulWidget {
|
class WalletRestoreFromKeysFrom extends StatefulWidget {
|
||||||
WalletRestoreFromKeysFrom({Key key}) : super(key: key);
|
WalletRestoreFromKeysFrom({Key key, this.onHeightOrDateEntered})
|
||||||
|
: super(key: key);
|
||||||
|
|
||||||
|
final Function (bool) onHeightOrDateEntered;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
WalletRestoreFromKeysFromState createState() =>
|
WalletRestoreFromKeysFromState createState() =>
|
||||||
|
@ -63,7 +66,9 @@ class WalletRestoreFromKeysFromState extends State<WalletRestoreFromKeysFrom> {
|
||||||
hintText: S.of(context).restore_spend_key_private,
|
hintText: S.of(context).restore_spend_key_private,
|
||||||
maxLines: null)),
|
maxLines: null)),
|
||||||
BlockchainHeightWidget(
|
BlockchainHeightWidget(
|
||||||
key: blockchainHeightKey, onHeightChange: (_) => null)
|
key: blockchainHeightKey,
|
||||||
|
onHeightChange: (_) => null,
|
||||||
|
onHeightOrDateEntered: widget.onHeightOrDateEntered)
|
||||||
]),
|
]),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,10 +7,12 @@ import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
|
||||||
import 'package:cake_wallet/src/widgets/blockchain_height_widget.dart';
|
import 'package:cake_wallet/src/widgets/blockchain_height_widget.dart';
|
||||||
|
|
||||||
class WalletRestoreFromSeedForm extends StatefulWidget {
|
class WalletRestoreFromSeedForm extends StatefulWidget {
|
||||||
WalletRestoreFromSeedForm({Key key, this.blockHeightFocusNode})
|
WalletRestoreFromSeedForm({Key key, this.blockHeightFocusNode,
|
||||||
|
this.onHeightOrDateEntered})
|
||||||
: super(key: key);
|
: super(key: key);
|
||||||
|
|
||||||
final FocusNode blockHeightFocusNode;
|
final FocusNode blockHeightFocusNode;
|
||||||
|
final Function (bool) onHeightOrDateEntered;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
WalletRestoreFromSeedFormState createState() =>
|
WalletRestoreFromSeedFormState createState() =>
|
||||||
|
@ -63,7 +65,8 @@ class WalletRestoreFromSeedFormState extends State<WalletRestoreFromSeedForm> {
|
||||||
readOnly: true)))),
|
readOnly: true)))),
|
||||||
BlockchainHeightWidget(
|
BlockchainHeightWidget(
|
||||||
focusNode: widget.blockHeightFocusNode,
|
focusNode: widget.blockHeightFocusNode,
|
||||||
key: blockchainHeightKey)
|
key: blockchainHeightKey,
|
||||||
|
onHeightOrDateEntered: widget.onHeightOrDateEntered)
|
||||||
]));
|
]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,8 +28,12 @@ class WalletRestorePage extends BasePage {
|
||||||
_pages.addAll([
|
_pages.addAll([
|
||||||
WalletRestoreFromSeedForm(
|
WalletRestoreFromSeedForm(
|
||||||
key: walletRestoreFromSeedFormKey,
|
key: walletRestoreFromSeedFormKey,
|
||||||
blockHeightFocusNode: _blockHeightFocusNode),
|
blockHeightFocusNode: _blockHeightFocusNode,
|
||||||
WalletRestoreFromKeysFrom(key: walletRestoreFromKeysFormKey)
|
onHeightOrDateEntered: (value)
|
||||||
|
=> walletRestoreViewModel.isButtonEnabled = value),
|
||||||
|
WalletRestoreFromKeysFrom(key: walletRestoreFromKeysFormKey,
|
||||||
|
onHeightOrDateEntered: (value)
|
||||||
|
=> walletRestoreViewModel.isButtonEnabled = value)
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,6 +76,21 @@ class WalletRestorePage extends BasePage {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
reaction((_) => walletRestoreViewModel.mode, (WalletRestoreMode mode)
|
||||||
|
{
|
||||||
|
walletRestoreViewModel.isButtonEnabled = false;
|
||||||
|
|
||||||
|
walletRestoreFromSeedFormKey.currentState.blockchainHeightKey
|
||||||
|
.currentState.restoreHeightController.text = '';
|
||||||
|
walletRestoreFromSeedFormKey.currentState.blockchainHeightKey
|
||||||
|
.currentState.dateController.text = '';
|
||||||
|
|
||||||
|
walletRestoreFromKeysFormKey.currentState.blockchainHeightKey
|
||||||
|
.currentState.restoreHeightController.text = '';
|
||||||
|
walletRestoreFromKeysFormKey.currentState.blockchainHeightKey
|
||||||
|
.currentState.dateController.text = '';
|
||||||
|
});
|
||||||
|
|
||||||
return Column(mainAxisAlignment: MainAxisAlignment.center, children: [
|
return Column(mainAxisAlignment: MainAxisAlignment.center, children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: PageView.builder(
|
child: PageView.builder(
|
||||||
|
@ -113,7 +132,8 @@ class WalletRestorePage extends BasePage {
|
||||||
.accentTextTheme
|
.accentTextTheme
|
||||||
.headline
|
.headline
|
||||||
.decorationColor,
|
.decorationColor,
|
||||||
isLoading: walletRestoreViewModel.state is IsExecutingState);
|
isLoading: walletRestoreViewModel.state is IsExecutingState,
|
||||||
|
isDisabled: !walletRestoreViewModel.isButtonEnabled,);
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
]);
|
]);
|
||||||
|
|
|
@ -6,10 +6,12 @@ import 'package:cake_wallet/monero/get_height_by_date.dart';
|
||||||
import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
|
import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
|
||||||
|
|
||||||
class BlockchainHeightWidget extends StatefulWidget {
|
class BlockchainHeightWidget extends StatefulWidget {
|
||||||
BlockchainHeightWidget({GlobalKey key, this.onHeightChange, this.focusNode})
|
BlockchainHeightWidget({GlobalKey key, this.onHeightChange, this.focusNode,
|
||||||
|
this.onHeightOrDateEntered})
|
||||||
: super(key: key);
|
: super(key: key);
|
||||||
|
|
||||||
final Function(int) onHeightChange;
|
final Function(int) onHeightChange;
|
||||||
|
final Function(bool) onHeightOrDateEntered;
|
||||||
final FocusNode focusNode;
|
final FocusNode focusNode;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -26,6 +28,13 @@ class BlockchainHeightState extends State<BlockchainHeightWidget> {
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
restoreHeightController.addListener(() {
|
restoreHeightController.addListener(() {
|
||||||
|
if (restoreHeightController.text.isNotEmpty) {
|
||||||
|
widget.onHeightOrDateEntered?.call(true);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
widget.onHeightOrDateEntered?.call(false);
|
||||||
|
dateController.text = '';
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
_changeHeight(restoreHeightController.text != null &&
|
_changeHeight(restoreHeightController.text != null &&
|
||||||
restoreHeightController.text.isNotEmpty
|
restoreHeightController.text.isNotEmpty
|
||||||
|
@ -83,6 +92,18 @@ class BlockchainHeightState extends State<BlockchainHeightWidget> {
|
||||||
))
|
))
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.only(left: 40, right: 40, top: 24),
|
||||||
|
child: Text(
|
||||||
|
S.of(context).restore_from_date_or_blockheight,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight: FontWeight.normal,
|
||||||
|
color: Theme.of(context).hintColor
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -92,7 +113,7 @@ class BlockchainHeightState extends State<BlockchainHeightWidget> {
|
||||||
final date = await getDate(
|
final date = await getDate(
|
||||||
context: context,
|
context: context,
|
||||||
initialDate: now.subtract(Duration(days: 1)),
|
initialDate: now.subtract(Duration(days: 1)),
|
||||||
firstDate: DateTime(2014, DateTime.april),
|
firstDate: DateTime(2014, DateTime.may),
|
||||||
lastDate: now);
|
lastDate: now);
|
||||||
|
|
||||||
if (date != null) {
|
if (date != null) {
|
||||||
|
|
|
@ -77,7 +77,7 @@ class SeedWidgetState extends State<SeedWidget> {
|
||||||
fontSize: 16.0, color: Theme.of(context).hintColor))),
|
fontSize: 16.0, color: Theme.of(context).hintColor))),
|
||||||
Padding(
|
Padding(
|
||||||
padding: EdgeInsets.only(right: 40, top: 10),
|
padding: EdgeInsets.only(right: 40, top: 10),
|
||||||
child: ValidableAnnotatedEditableText(
|
child: ValidatableAnnotatedEditableText(
|
||||||
cursorColor: Colors.blue,
|
cursorColor: Colors.blue,
|
||||||
backgroundCursorColor: Colors.blue,
|
backgroundCursorColor: Colors.blue,
|
||||||
validStyle: TextStyle(
|
validStyle: TextStyle(
|
||||||
|
|
|
@ -22,8 +22,8 @@ class TextAnnotation extends Comparable<TextAnnotation> {
|
||||||
int compareTo(TextAnnotation other) => text.compareTo(other.text);
|
int compareTo(TextAnnotation other) => text.compareTo(other.text);
|
||||||
}
|
}
|
||||||
|
|
||||||
class ValidableAnnotatedEditableText extends EditableText {
|
class ValidatableAnnotatedEditableText extends EditableText {
|
||||||
ValidableAnnotatedEditableText({
|
ValidatableAnnotatedEditableText({
|
||||||
Key key,
|
Key key,
|
||||||
FocusNode focusNode,
|
FocusNode focusNode,
|
||||||
TextEditingController controller,
|
TextEditingController controller,
|
||||||
|
@ -49,7 +49,7 @@ class ValidableAnnotatedEditableText extends EditableText {
|
||||||
controller: controller,
|
controller: controller,
|
||||||
cursorColor: cursorColor,
|
cursorColor: cursorColor,
|
||||||
style: validStyle,
|
style: validStyle,
|
||||||
keyboardType: TextInputType.text,
|
keyboardType: TextInputType.visiblePassword,
|
||||||
autocorrect: false,
|
autocorrect: false,
|
||||||
autofocus: false,
|
autofocus: false,
|
||||||
selectionColor: selectionColor,
|
selectionColor: selectionColor,
|
||||||
|
@ -73,14 +73,14 @@ class ValidableAnnotatedEditableText extends EditableText {
|
||||||
final TextStyle invalidStyle;
|
final TextStyle invalidStyle;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
ValidableAnnotatedEditableTextState createState() =>
|
ValidatableAnnotatedEditableTextState createState() =>
|
||||||
ValidableAnnotatedEditableTextState();
|
ValidatableAnnotatedEditableTextState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class ValidableAnnotatedEditableTextState extends EditableTextState {
|
class ValidatableAnnotatedEditableTextState extends EditableTextState {
|
||||||
@override
|
@override
|
||||||
ValidableAnnotatedEditableText get widget =>
|
ValidatableAnnotatedEditableText get widget =>
|
||||||
super.widget as ValidableAnnotatedEditableText;
|
super.widget as ValidatableAnnotatedEditableText;
|
||||||
|
|
||||||
List<Annotation> getRanges() {
|
List<Annotation> getRanges() {
|
||||||
final result = List<Annotation>();
|
final result = List<Annotation>();
|
||||||
|
|
|
@ -110,6 +110,8 @@ abstract class AuthViewModelBase with Store {
|
||||||
|
|
||||||
if (isAuthenticated) {
|
if (isAuthenticated) {
|
||||||
state = ExecutedSuccessfullyState();
|
state = ExecutedSuccessfullyState();
|
||||||
|
} else {
|
||||||
|
state = FailureState('Failure biometric authentication');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
|
|
|
@ -78,9 +78,8 @@ abstract class DashboardViewModelBase with Store {
|
||||||
.transactionHistory.transactions.values
|
.transactionHistory.transactions.values
|
||||||
.map((transaction) => TransactionListItem(
|
.map((transaction) => TransactionListItem(
|
||||||
transaction: transaction,
|
transaction: transaction,
|
||||||
price: price,
|
balanceViewModel: balanceViewModel,
|
||||||
fiatCurrency: appStore.settingsStore.fiatCurrency,
|
settingsStore: appStore.settingsStore)));
|
||||||
displayMode: balanceDisplayMode)));
|
|
||||||
|
|
||||||
_reaction = reaction((_) => appStore.wallet, _onWalletChange);
|
_reaction = reaction((_) => appStore.wallet, _onWalletChange);
|
||||||
// FIXME: fixme
|
// FIXME: fixme
|
||||||
|
@ -89,9 +88,8 @@ abstract class DashboardViewModelBase with Store {
|
||||||
transactions,
|
transactions,
|
||||||
(TransactionInfo val) => TransactionListItem(
|
(TransactionInfo val) => TransactionListItem(
|
||||||
transaction: val,
|
transaction: val,
|
||||||
price: price,
|
balanceViewModel: balanceViewModel,
|
||||||
fiatCurrency: appStore.settingsStore.fiatCurrency,
|
settingsStore: appStore.settingsStore));
|
||||||
displayMode: balanceDisplayMode));
|
|
||||||
|
|
||||||
final _wallet = wallet;
|
final _wallet = wallet;
|
||||||
|
|
||||||
|
@ -185,8 +183,7 @@ abstract class DashboardViewModelBase with Store {
|
||||||
transactions.addAll(wallet.transactionHistory.transactions.values.map(
|
transactions.addAll(wallet.transactionHistory.transactions.values.map(
|
||||||
(transaction) => TransactionListItem(
|
(transaction) => TransactionListItem(
|
||||||
transaction: transaction,
|
transaction: transaction,
|
||||||
price: price,
|
balanceViewModel: balanceViewModel,
|
||||||
fiatCurrency: appStore.settingsStore.fiatCurrency,
|
settingsStore: appStore.settingsStore)));
|
||||||
displayMode: balanceDisplayMode)));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import 'package:cake_wallet/entities/balance_display_mode.dart';
|
import 'package:cake_wallet/entities/balance_display_mode.dart';
|
||||||
import 'package:cake_wallet/entities/fiat_currency.dart';
|
import 'package:cake_wallet/entities/fiat_currency.dart';
|
||||||
import 'package:cake_wallet/entities/transaction_info.dart';
|
import 'package:cake_wallet/entities/transaction_info.dart';
|
||||||
|
import 'package:cake_wallet/store/settings_store.dart';
|
||||||
import 'package:cake_wallet/utils/mobx.dart';
|
import 'package:cake_wallet/utils/mobx.dart';
|
||||||
import 'package:cake_wallet/view_model/dashboard/action_list_item.dart';
|
import 'package:cake_wallet/view_model/dashboard/action_list_item.dart';
|
||||||
import 'package:cake_wallet/bitcoin/bitcoin_transaction_info.dart';
|
import 'package:cake_wallet/bitcoin/bitcoin_transaction_info.dart';
|
||||||
|
@ -8,15 +9,21 @@ import 'package:cake_wallet/monero/monero_transaction_info.dart';
|
||||||
import 'package:cake_wallet/monero/monero_amount_format.dart';
|
import 'package:cake_wallet/monero/monero_amount_format.dart';
|
||||||
import 'package:cake_wallet/bitcoin/bitcoin_amount_format.dart';
|
import 'package:cake_wallet/bitcoin/bitcoin_amount_format.dart';
|
||||||
import 'package:cake_wallet/entities/calculate_fiat_amount_raw.dart';
|
import 'package:cake_wallet/entities/calculate_fiat_amount_raw.dart';
|
||||||
|
import 'package:cake_wallet/view_model/dashboard/balance_view_model.dart';
|
||||||
|
|
||||||
class TransactionListItem extends ActionListItem with Keyable {
|
class TransactionListItem extends ActionListItem with Keyable {
|
||||||
TransactionListItem(
|
TransactionListItem(
|
||||||
{this.transaction, this.price, this.fiatCurrency, this.displayMode});
|
{this.transaction, this.balanceViewModel, this.settingsStore});
|
||||||
|
|
||||||
final TransactionInfo transaction;
|
final TransactionInfo transaction;
|
||||||
final double price;
|
final BalanceViewModel balanceViewModel;
|
||||||
final FiatCurrency fiatCurrency;
|
final SettingsStore settingsStore;
|
||||||
final BalanceDisplayMode displayMode;
|
|
||||||
|
double get price => balanceViewModel.price;
|
||||||
|
|
||||||
|
FiatCurrency get fiatCurrency => settingsStore.fiatCurrency;
|
||||||
|
|
||||||
|
BalanceDisplayMode get displayMode => settingsStore.balanceDisplayMode;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
dynamic get keyIndex => transaction.id;
|
dynamic get keyIndex => transaction.id;
|
||||||
|
@ -49,4 +56,4 @@ class TransactionListItem extends ActionListItem with Keyable {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
DateTime get date => transaction.date;
|
DateTime get date => transaction.date;
|
||||||
}
|
}
|
|
@ -10,12 +10,16 @@ enum RescanWalletState { rescaning, none }
|
||||||
abstract class RescanViewModelBase with Store {
|
abstract class RescanViewModelBase with Store {
|
||||||
RescanViewModelBase(this._wallet) {
|
RescanViewModelBase(this._wallet) {
|
||||||
state = RescanWalletState.none;
|
state = RescanWalletState.none;
|
||||||
|
isButtonEnabled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@observable
|
@observable
|
||||||
RescanWalletState state;
|
RescanWalletState state;
|
||||||
final WalletBase _wallet;
|
final WalletBase _wallet;
|
||||||
|
|
||||||
|
@observable
|
||||||
|
bool isButtonEnabled;
|
||||||
|
|
||||||
@action
|
@action
|
||||||
Future<void> rescanCurrentWallet({int restoreHeight}) async {
|
Future<void> rescanCurrentWallet({int restoreHeight}) async {
|
||||||
state = RescanWalletState.rescaning;
|
state = RescanWalletState.rescaning;
|
||||||
|
|
|
@ -23,6 +23,7 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
|
||||||
Box<WalletInfo> walletInfoSource,
|
Box<WalletInfo> walletInfoSource,
|
||||||
{@required WalletType type})
|
{@required WalletType type})
|
||||||
: super(appStore, walletInfoSource, type: type, isRecovery: true) {
|
: super(appStore, walletInfoSource, type: type, isRecovery: true) {
|
||||||
|
isButtonEnabled = false;
|
||||||
mode = WalletRestoreMode.seed;
|
mode = WalletRestoreMode.seed;
|
||||||
_walletCreationService.changeWalletType(type: WalletType.monero);
|
_walletCreationService.changeWalletType(type: WalletType.monero);
|
||||||
}
|
}
|
||||||
|
@ -30,6 +31,9 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
|
||||||
@observable
|
@observable
|
||||||
WalletRestoreMode mode;
|
WalletRestoreMode mode;
|
||||||
|
|
||||||
|
@observable
|
||||||
|
bool isButtonEnabled;
|
||||||
|
|
||||||
final WalletCreationService _walletCreationService;
|
final WalletCreationService _walletCreationService;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
|
@ -401,10 +401,10 @@ packages:
|
||||||
description:
|
description:
|
||||||
path: "."
|
path: "."
|
||||||
ref: cake
|
ref: cake
|
||||||
resolved-ref: a734c2ea3239f9153dba6f5bec740e1df54ee754
|
resolved-ref: d4d68a9c1e4c45eb236cd7a5a2fac84c394a7605
|
||||||
url: "https://github.com/cake-tech/flutter_secure_storage.git"
|
url: "https://github.com/cake-tech/flutter_secure_storage.git"
|
||||||
source: git
|
source: git
|
||||||
version: "3.3.55"
|
version: "3.3.57"
|
||||||
flutter_slidable:
|
flutter_slidable:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -498,7 +498,7 @@ packages:
|
||||||
name: image
|
name: image
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.18"
|
version: "2.1.19"
|
||||||
intl:
|
intl:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -1000,7 +1000,7 @@ packages:
|
||||||
name: win32
|
name: win32
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.7.3"
|
version: "1.7.4"
|
||||||
xdg_directories:
|
xdg_directories:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
@ -11,7 +11,7 @@ description: Cake Wallet.
|
||||||
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
|
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
|
||||||
# Read more about iOS versioning at
|
# Read more about iOS versioning at
|
||||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||||
version: 4.0.4+14
|
version: 4.0.8+20
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=2.7.0 <3.0.0"
|
sdk: ">=2.7.0 <3.0.0"
|
||||||
|
@ -31,6 +31,7 @@ dependencies:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/cake-tech/flutter_secure_storage.git
|
url: https://github.com/cake-tech/flutter_secure_storage.git
|
||||||
ref: cake
|
ref: cake
|
||||||
|
version: 3.3.57
|
||||||
provider: ^3.1.0
|
provider: ^3.1.0
|
||||||
rxdart: ^0.22.2
|
rxdart: ^0.22.2
|
||||||
yaml: ^2.1.16
|
yaml: ^2.1.16
|
||||||
|
|
Loading…
Reference in a new issue