mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2024-11-17 09:47:35 +00:00
Merge branch '4.0.8' into CAKE-158-transaction-and-balance-displaying-between-accounts
# Conflicts: # lib/src/screens/receive/receive_page.dart
This commit is contained in:
commit
17ba74bce7
17 changed files with 187 additions and 93 deletions
|
@ -1,5 +1,5 @@
|
|||
# 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.
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
|
||||
<application
|
||||
android:label="Cake Wallet"
|
||||
android:allowBackup="false"
|
||||
android:fullBackupContent="false"
|
||||
android:icon="@mipmap/ic_launcher">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
|
|
|
@ -371,7 +371,7 @@
|
|||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/Flutter",
|
||||
);
|
||||
MARKETING_VERSION = 4.0.3;
|
||||
MARKETING_VERSION = 4.0.8;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.fotolockr.cakewallet;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||
|
@ -511,7 +511,7 @@
|
|||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/Flutter",
|
||||
);
|
||||
MARKETING_VERSION = 4.0.3;
|
||||
MARKETING_VERSION = 4.0.8;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.fotolockr.cakewallet;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||
|
@ -545,7 +545,7 @@
|
|||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/Flutter",
|
||||
);
|
||||
MARKETING_VERSION = 4.0.3;
|
||||
MARKETING_VERSION = 4.0.8;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.fotolockr.cakewallet;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||
|
|
|
@ -204,7 +204,8 @@ Future setup(
|
|||
authPageState.changeProcessText('Loading the wallet');
|
||||
|
||||
if (loginError != null) {
|
||||
authPageState.changeProcessText('ERROR: ${loginError.toString()}');
|
||||
authPageState
|
||||
.changeProcessText('ERROR: ${loginError.toString()}');
|
||||
}
|
||||
|
||||
ReactionDisposer _reaction;
|
||||
|
|
|
@ -19,3 +19,10 @@ Future<String> pathForWalletDir({@required String name, @required WalletType ty
|
|||
Future<String> pathForWallet({@required String name, @required WalletType type}) async =>
|
||||
await pathForWalletDir(name: name, type: type)
|
||||
.then((path) => path + '/$name');
|
||||
|
||||
Future<String> outdatedAndroidPathForWalletDir({String name}) async {
|
||||
final directory = await getApplicationDocumentsDirectory();
|
||||
final pathDir = directory.path + '/$name';
|
||||
|
||||
return pathDir;
|
||||
}
|
|
@ -20,7 +20,7 @@ abstract class MoneroAccountListBase with Store {
|
|||
bool _isRefreshing;
|
||||
bool _isUpdating;
|
||||
|
||||
Future update() async {
|
||||
void update() async {
|
||||
if (_isUpdating) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -125,8 +125,8 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance> with Store {
|
|||
_onAccountChangeReaction?.reaction?.dispose();
|
||||
}
|
||||
|
||||
Future<bool> validate() async {
|
||||
await accountList.update();
|
||||
bool validate() {
|
||||
accountList.update();
|
||||
final accountListLength = accountList.accounts?.length ?? 0;
|
||||
|
||||
if (accountListLength <= 0) {
|
||||
|
|
|
@ -27,7 +27,7 @@ class MoneroRestoreWalletFromSeedCredentials extends WalletCredentials {
|
|||
|
||||
class MoneroWalletLoadingException implements Exception {
|
||||
@override
|
||||
String toString() => 'The wallet is damaged.';
|
||||
String toString() => 'Failure to load the wallet.';
|
||||
}
|
||||
|
||||
class MoneroRestoreWalletFromKeysCredentials extends WalletCredentials {
|
||||
|
@ -93,6 +93,11 @@ class MoneroWalletService extends WalletService<
|
|||
Future<MoneroWallet> openWallet(String name, String password) async {
|
||||
try {
|
||||
final path = await pathForWallet(name: name, type: WalletType.monero);
|
||||
|
||||
if (!File(path).existsSync()) {
|
||||
await repairOldAndroidWallet(name);
|
||||
}
|
||||
|
||||
await monero_wallet_manager
|
||||
.openWalletAsync({'path': path, 'password': password});
|
||||
final walletInfo = walletInfoSource.values.firstWhere(
|
||||
|
@ -100,17 +105,17 @@ class MoneroWalletService extends WalletService<
|
|||
orElse: () => null);
|
||||
final wallet = MoneroWallet(
|
||||
filename: monero_wallet.getFilename(), walletInfo: walletInfo);
|
||||
final isValid = await wallet.validate();
|
||||
final isValid = wallet.validate();
|
||||
|
||||
if (!isValid) {
|
||||
// if (wallet.seed?.isNotEmpty ?? false) {
|
||||
// let restore from seed in this case;
|
||||
// final seed = wallet.seed;
|
||||
// final credentials = MoneroRestoreWalletFromSeedCredentials(
|
||||
// name: name, password: password, mnemonic: seed, height: 2000000)
|
||||
// ..walletInfo = walletInfo;
|
||||
// await remove(name);
|
||||
// return restoreFromSeed(credentials);
|
||||
// let restore from seed in this case;
|
||||
// final seed = wallet.seed;
|
||||
// final credentials = MoneroRestoreWalletFromSeedCredentials(
|
||||
// name: name, password: password, mnemonic: seed, height: 2000000)
|
||||
// ..walletInfo = walletInfo;
|
||||
// await remove(name);
|
||||
// return restoreFromSeed(credentials);
|
||||
// }
|
||||
|
||||
throw MoneroWalletLoadingException();
|
||||
|
@ -187,4 +192,38 @@ class MoneroWalletService extends WalletService<
|
|||
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/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 {
|
||||
PendingMoneroTransaction(this.pendingTransactionDescription);
|
||||
|
||||
|
@ -22,7 +30,18 @@ class PendingMoneroTransaction with PendingTransaction {
|
|||
CryptoCurrency.xmr, pendingTransactionDescription.fee);
|
||||
|
||||
@override
|
||||
Future<void> commit() async =>
|
||||
Future<void> commit() async {
|
||||
try {
|
||||
monero_transaction_history.commitTransactionFromPointerAddress(
|
||||
address: pendingTransactionDescription.pointerAddress);
|
||||
} catch (e) {
|
||||
final message = e.toString();
|
||||
|
||||
if (message.contains('Reason: double spend')) {
|
||||
throw DoubleSpendException();
|
||||
}
|
||||
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,13 +40,14 @@ class AuthPageState extends State<AuthPage> {
|
|||
reaction((_) => widget.authViewModel.state, (ExecutionState state) {
|
||||
if (state is ExecutedSuccessfullyState) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
_authBar?.dismiss();
|
||||
if (widget.onAuthenticationFinished != null) {
|
||||
widget.onAuthenticationFinished(true, this);
|
||||
} else {
|
||||
_authBar?.dismiss();
|
||||
showBar<void>(context, S.of(context).authenticated);
|
||||
}
|
||||
});
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
if (state is IsExecutingState) {
|
||||
|
|
|
@ -15,9 +15,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_view_model.dart';
|
||||
import 'package:cake_wallet/src/screens/receive/widgets/qr_widget.dart';
|
||||
import 'package:keyboard_actions/keyboard_actions.dart';
|
||||
|
||||
class ReceivePage extends BasePage {
|
||||
ReceivePage({this.addressListViewModel});
|
||||
ReceivePage({this.addressListViewModel}) : _cryptoAmountFocus = FocusNode();
|
||||
|
||||
final WalletAddressListViewModel addressListViewModel;
|
||||
|
||||
|
@ -33,6 +34,8 @@ class ReceivePage extends BasePage {
|
|||
@override
|
||||
Color get titleColor => Colors.white;
|
||||
|
||||
final FocusNode _cryptoAmountFocus;
|
||||
|
||||
@override
|
||||
Widget Function(BuildContext, Widget) get rootWrapper =>
|
||||
(BuildContext context, Widget scaffold) => Container(
|
||||
|
@ -67,53 +70,68 @@ class ReceivePage extends BasePage {
|
|||
|
||||
@override
|
||||
Widget body(BuildContext context) {
|
||||
return SingleChildScrollView(
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: EdgeInsets.fromLTRB(24, 80, 24, 40),
|
||||
child: QRWidget(
|
||||
addressListViewModel: addressListViewModel,
|
||||
isAmountFieldShow: true,
|
||||
),
|
||||
),
|
||||
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();
|
||||
return KeyboardActions(
|
||||
config: KeyboardActionsConfig(
|
||||
keyboardActionsPlatform: KeyboardActionsPlatform.IOS,
|
||||
keyboardBarColor: isDarkTheme
|
||||
? Color.fromRGBO(48, 51, 60, 1.0)
|
||||
: Color.fromRGBO(98, 98, 98, 1.0),
|
||||
nextFocus: false,
|
||||
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 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 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) {
|
||||
final isFirst = addressListViewModel.isFirstAddress;
|
||||
|
@ -146,17 +164,17 @@ class ReceivePage extends BasePage {
|
|||
});
|
||||
}
|
||||
|
||||
return index != 0
|
||||
? cell
|
||||
: ClipRRect(
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(30),
|
||||
topRight: Radius.circular(30)),
|
||||
child: cell,
|
||||
);
|
||||
})),
|
||||
],
|
||||
),
|
||||
);
|
||||
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 {
|
||||
QRWidget(
|
||||
{@required this.addressListViewModel, this.isAmountFieldShow = false})
|
||||
{@required this.addressListViewModel,
|
||||
this.isAmountFieldShow = false,
|
||||
this.amountTextFieldFocusNode})
|
||||
: amountController = TextEditingController(),
|
||||
_formKey = GlobalKey<FormState>() {
|
||||
amountController.addListener(() => addressListViewModel.amount =
|
||||
|
@ -21,6 +23,7 @@ class QRWidget extends StatelessWidget {
|
|||
final WalletAddressListViewModel addressListViewModel;
|
||||
final bool isAmountFieldShow;
|
||||
final TextEditingController amountController;
|
||||
final FocusNode amountTextFieldFocusNode;
|
||||
final GlobalKey<FormState> _formKey;
|
||||
|
||||
@override
|
||||
|
@ -45,7 +48,7 @@ class QRWidget extends StatelessWidget {
|
|||
data: addressListViewModel.uri.toString(),
|
||||
backgroundColor: Colors.transparent,
|
||||
foregroundColor: Colors.white,
|
||||
//Theme.of(context).textTheme.headline.color,
|
||||
//Theme.of(context).textTheme.headline.color,
|
||||
))))),
|
||||
Spacer(flex: 3)
|
||||
]),
|
||||
|
@ -68,6 +71,7 @@ class QRWidget extends StatelessWidget {
|
|||
child: Form(
|
||||
key: _formKey,
|
||||
child: BaseTextFormField(
|
||||
focusNode: amountTextFieldFocusNode,
|
||||
controller: amountController,
|
||||
keyboardType: TextInputType.numberWithOptions(
|
||||
decimal: true),
|
||||
|
|
|
@ -77,7 +77,7 @@ class SeedWidgetState extends State<SeedWidget> {
|
|||
fontSize: 16.0, color: Theme.of(context).hintColor))),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(right: 40, top: 10),
|
||||
child: ValidableAnnotatedEditableText(
|
||||
child: ValidatableAnnotatedEditableText(
|
||||
cursorColor: Colors.blue,
|
||||
backgroundCursorColor: Colors.blue,
|
||||
validStyle: TextStyle(
|
||||
|
|
|
@ -22,8 +22,8 @@ class TextAnnotation extends Comparable<TextAnnotation> {
|
|||
int compareTo(TextAnnotation other) => text.compareTo(other.text);
|
||||
}
|
||||
|
||||
class ValidableAnnotatedEditableText extends EditableText {
|
||||
ValidableAnnotatedEditableText({
|
||||
class ValidatableAnnotatedEditableText extends EditableText {
|
||||
ValidatableAnnotatedEditableText({
|
||||
Key key,
|
||||
FocusNode focusNode,
|
||||
TextEditingController controller,
|
||||
|
@ -49,7 +49,7 @@ class ValidableAnnotatedEditableText extends EditableText {
|
|||
controller: controller,
|
||||
cursorColor: cursorColor,
|
||||
style: validStyle,
|
||||
keyboardType: TextInputType.text,
|
||||
keyboardType: TextInputType.visiblePassword,
|
||||
autocorrect: false,
|
||||
autofocus: false,
|
||||
selectionColor: selectionColor,
|
||||
|
@ -73,14 +73,14 @@ class ValidableAnnotatedEditableText extends EditableText {
|
|||
final TextStyle invalidStyle;
|
||||
|
||||
@override
|
||||
ValidableAnnotatedEditableTextState createState() =>
|
||||
ValidableAnnotatedEditableTextState();
|
||||
ValidatableAnnotatedEditableTextState createState() =>
|
||||
ValidatableAnnotatedEditableTextState();
|
||||
}
|
||||
|
||||
class ValidableAnnotatedEditableTextState extends EditableTextState {
|
||||
class ValidatableAnnotatedEditableTextState extends EditableTextState {
|
||||
@override
|
||||
ValidableAnnotatedEditableText get widget =>
|
||||
super.widget as ValidableAnnotatedEditableText;
|
||||
ValidatableAnnotatedEditableText get widget =>
|
||||
super.widget as ValidatableAnnotatedEditableText;
|
||||
|
||||
List<Annotation> getRanges() {
|
||||
final result = List<Annotation>();
|
||||
|
|
|
@ -110,6 +110,8 @@ abstract class AuthViewModelBase with Store {
|
|||
|
||||
if (isAuthenticated) {
|
||||
state = ExecutedSuccessfullyState();
|
||||
} else {
|
||||
state = FailureState('Failure biometric authentication');
|
||||
}
|
||||
}
|
||||
} catch(e) {
|
||||
|
|
|
@ -401,10 +401,10 @@ packages:
|
|||
description:
|
||||
path: "."
|
||||
ref: cake
|
||||
resolved-ref: a734c2ea3239f9153dba6f5bec740e1df54ee754
|
||||
resolved-ref: d4d68a9c1e4c45eb236cd7a5a2fac84c394a7605
|
||||
url: "https://github.com/cake-tech/flutter_secure_storage.git"
|
||||
source: git
|
||||
version: "3.3.55"
|
||||
version: "3.3.57"
|
||||
flutter_slidable:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -498,7 +498,7 @@ packages:
|
|||
name: image
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.1.18"
|
||||
version: "2.1.19"
|
||||
intl:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -1000,7 +1000,7 @@ packages:
|
|||
name: win32
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.7.3"
|
||||
version: "1.7.4"
|
||||
xdg_directories:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
|
@ -11,7 +11,7 @@ description: Cake Wallet.
|
|||
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
|
||||
# Read more about iOS versioning at
|
||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||
version: 4.0.4+14
|
||||
version: 4.0.8+20
|
||||
|
||||
environment:
|
||||
sdk: ">=2.7.0 <3.0.0"
|
||||
|
@ -31,6 +31,7 @@ dependencies:
|
|||
git:
|
||||
url: https://github.com/cake-tech/flutter_secure_storage.git
|
||||
ref: cake
|
||||
version: 3.3.57
|
||||
provider: ^3.1.0
|
||||
rxdart: ^0.22.2
|
||||
yaml: ^2.1.16
|
||||
|
|
Loading…
Reference in a new issue