TMP 4
M committed
Jun 20, 2020 at 10:10 UTC
81cee186dbc7c7a78d37d96ef1d18666e3d094ea
94 files changed
+3771
-2986
.gitignore
+3
-1
@@ -87,4 +87,6 @@ cw_monero/cw_monero/android/.cxx/
87
android/key.properties
88
89
**/tool/.secrets-prod.json
90
-**/lib/.secrets.g.dart
\ No newline at end of file
90
+**/lib/.secrets.g.dart
91
+
92
+vendor/
\ No newline at end of file
.gitmodules
new
+6
@@ -0,0 +1,6 @@
1
+[submodule "inject.dart"]
2
+ path = inject.dart
3
+ url = https://github.com/google/inject.dart
4
+[submodule ".vendor/inject.dart"]
5
+ path = .vendor/inject.dart
6
+ url = https://github.com/google/inject.dart
lib/bitcoin/bitcoin_address_record.dart
new
+17
@@ -0,0 +1,17 @@
1
+import 'dart:convert';
2
+
3
+class BitcoinAddressRecord {
4
+ BitcoinAddressRecord(this.address, {this.label});
5
+
6
+ factory BitcoinAddressRecord.fromJSON(String jsonSource) {
7
+ final decoded = json.decode(jsonSource) as Map;
8
+
9
+ return BitcoinAddressRecord(decoded['address'] as String,
10
+ label: decoded['label'] as String);
11
+ }
12
+
13
+ final String address;
14
+ String label;
15
+
16
+ String toJSON() => json.encode({'label': label, 'address': address});
17
+}
lib/bitcoin/bitcoin_balance.dart
+22
-1
@@ -1,14 +1,35 @@
1
+import 'dart:convert';
2
+
3
import 'package:flutter/foundation.dart';
4
import 'package:cake_wallet/bitcoin/bitcoin_amount_format.dart';
5
import 'package:cake_wallet/src/domain/common/balance.dart';
6
7
class BitcoinBalance extends Balance {
6
- BitcoinBalance({@required this.confirmed, @required this.unconfirmed});
8
+ const BitcoinBalance({@required this.confirmed, @required this.unconfirmed}) : super();
9
+
10
+ factory BitcoinBalance.fromJSON(String jsonSource) {
11
+ if (jsonSource == null) {
12
+ return null;
13
+ }
14
+
15
+ final decoded = json.decode(jsonSource) as Map;
16
+
17
+ return BitcoinBalance(
18
+ confirmed: decoded['confirmed'] as int ?? 0,
19
+ unconfirmed: decoded['unconfirmed'] as int ?? 0);
20
+ }
21
22
final int confirmed;
23
final int unconfirmed;
24
+
25
int get total => confirmed + unconfirmed;
26
+
27
String get confirmedFormatted => bitcoinAmountToString(amount: confirmed);
28
+
29
String get unconfirmedFormatted => bitcoinAmountToString(amount: unconfirmed);
30
+
31
String get totalFormatted => bitcoinAmountToString(amount: total);
32
+
33
+ String toJSON() =>
34
+ json.encode({'confirmed': confirmed, 'unconfirmed': unconfirmed});
35
}
lib/bitcoin/bitcoin_transaction_history.dart
+56
-70
@@ -1,78 +1,64 @@
1
import 'dart:convert';
2
-import 'package:cake_wallet/bitcoin/bitcoin_wallet.dart';
2
import 'package:flutter/foundation.dart';
4
-import 'package:rxdart/rxdart.dart';
3
+import 'package:mobx/mobx.dart';
4
+import 'package:cake_wallet/core/transaction_history.dart';
5
+import 'package:cake_wallet/bitcoin/file.dart';
6
+import 'package:cake_wallet/bitcoin/bitcoin_wallet.dart';
7
import 'package:cake_wallet/bitcoin/bitcoin_transaction_info.dart';
8
import 'package:cake_wallet/bitcoin/electrum.dart';
7
-import 'package:cake_wallet/src/domain/common/transaction_history.dart';
9
import 'package:cake_wallet/src/domain/common/transaction_info.dart';
9
-import 'package:cake_wallet/bitcoin/file.dart';
10
+import 'package:cake_wallet/src/domain/common/transaction_direction.dart';
11
+
12
+part 'bitcoin_transaction_history.g.dart';
13
11
-class BitcoinTransactionHistory extends TransactionHistory {
12
- BitcoinTransactionHistory(
13
- {@required this.eclient,
14
- @required this.path,
15
- @required String password,
16
- @required this.wallet})
17
- : _transactions = BehaviorSubject<List<TransactionInfo>>.seeded([]),
14
+// TODO: Think about another transaction store for bitcoin transaction history..
15
+
16
+const _transactionsHistoryFileName = 'transactions.json';
17
+
18
+class BitcoinTransactionHistory = BitcoinTransactionHistoryBase
19
+ with _$BitcoinTransactionHistory;
20
+
21
+abstract class BitcoinTransactionHistoryBase
22
+ extends TransactionHistoryBase<BitcoinTransactionInfo> with Store {
23
+ BitcoinTransactionHistoryBase(
24
+ {this.eclient, String dirPath, @required String password})
25
+ : path = '$dirPath/$_transactionsHistoryFileName',
26
_password = password,
27
_height = 0;
28
21
- final BitcoinWallet wallet;
29
+ BitcoinWallet wallet;
30
final ElectrumClient eclient;
31
final String path;
32
final String _password;
33
int _height;
34
27
- @override
28
- Observable<List<TransactionInfo>> get transactions => _transactions.stream;
29
- List<TransactionInfo> get transactionsAll => _transactions.value;
30
- final BehaviorSubject<List<TransactionInfo>> _transactions;
31
- bool _isUpdating = false;
32
-
35
Future<void> init() async {
36
+ // TODO: throw exeption if wallet is null;
37
final info = await _read();
38
_height = (info['height'] as int) ?? _height;
36
- _transactions.value = info['transactions'] as List<TransactionInfo>;
39
+ // FIXME: remove hardcoded value
40
+ transactions = ObservableList.of([
41
+ BitcoinTransactionInfo(
42
+ id: 'test',
43
+ height: 12,
44
+ amount: 12,
45
+ direction: TransactionDirection.incoming,
46
+ date: DateTime.now(),
47
+ isPending: false)
48
+ ]);
49
}
50
39
- @override
40
- Future<List<TransactionInfo>> getAll() async => _transactions.value;
41
-
51
@override
52
Future update() async {
44
- if (_isUpdating) {
45
- return;
46
- }
47
-
48
- try {
49
- _isUpdating = true;
50
- final newTransasctions = await fetchTransactions();
51
- _transactions.value = _transactions.value + newTransasctions;
52
- _updateHeight();
53
- await save();
54
- _isUpdating = false;
55
- } catch (e) {
56
- _isUpdating = false;
57
- rethrow;
58
- }
59
- }
60
-
61
- Future<Map<String, Object>> fetchTransactionInfo(
62
- {@required String hash, @required int height}) async {
63
- final rawFetching = eclient.getTransactionRaw(hash: hash);
64
- final headerFetching = eclient.getHeader(height: height);
65
- final result = await Future.wait([rawFetching, headerFetching]);
66
- final raw = result.first as String;
67
- final header = result[1] as Map<String, Object>;
68
-
69
- return {'raw': raw, 'header': header};
53
+ await super.update();
54
+ _updateHeight();
55
}
56
57
+ @override
58
Future<List<BitcoinTransactionInfo>> fetchTransactions() async {
73
- final addresses = wallet.getAddresses();
59
+ final addresses = wallet.addresses;
60
final histories =
75
- addresses.map((address) => eclient.getHistory(address: address));
61
+ addresses.map((record) => eclient.getHistory(address: record.address));
62
final _historiesWithDetails = await Future.wait(histories)
63
.then((histories) => histories
64
.map((h) => h.where((tx) => (tx['height'] as int) > _height))
@@ -85,35 +71,35 @@ class BitcoinTransactionHistory extends TransactionHistory {
71
return historiesWithDetails
72
.map((info) => BitcoinTransactionInfo.fromHexAndHeader(
73
info['raw'] as String, info['header'] as Map<String, Object>,
88
- addresses: addresses))
74
+ addresses: addresses.map((record) => record.address).toList()))
75
.toList();
76
}
77
78
+ Future<Map<String, Object>> fetchTransactionInfo(
79
+ {@required String hash, @required int height}) async {
80
+ final rawFetching = eclient.getTransactionRaw(hash: hash);
81
+ final headerFetching = eclient.getHeader(height: height);
82
+ final result = await Future.wait([rawFetching, headerFetching]);
83
+ final raw = result.first as String;
84
+ final header = result[1] as Map<String, Object>;
85
+
86
+ return {'raw': raw, 'header': header};
87
+ }
88
+
89
Future<void> add(List<BitcoinTransactionInfo> transactions) async {
93
- final txs = await getAll()
94
- ..addAll(transactions);
95
- await writeData(
96
- path: path,
97
- password: _password,
98
- data: json
99
- .encode(txs.map((tx) => (tx as BitcoinTransactionInfo).toJson())));
90
+ this.transactions.addAll(transactions);
91
+ await save();
92
}
93
94
Future<void> addOne(BitcoinTransactionInfo tx) async {
103
- final txs = await getAll()
104
- ..add(tx);
105
- await writeData(
106
- path: path,
107
- password: _password,
108
- data: json
109
- .encode(txs.map((tx) => (tx as BitcoinTransactionInfo).toJson())));
95
+ transactions.add(tx);
96
+ await save();
97
}
98
99
Future<void> save() async => writeData(
100
path: path,
101
password: _password,
115
- data: json
116
- .encode({'height': _height, 'transactions': _transactions.value}));
102
+ data: json.encode({'height': _height, 'transactions': transactions}));
103
104
Future<Map<String, Object>> _read() async {
105
try {
@@ -133,13 +119,13 @@ class BitcoinTransactionHistory extends TransactionHistory {
119
120
return {'transactions': transactions, 'height': height};
121
} catch (_) {
136
- return {'transactions': List<TransactionInfo>(), 'height': 0};
122
+ return {'transactions': <TransactionInfo>[], 'height': 0};
123
}
124
}
125
126
void _updateHeight() {
141
- final int newHeight = _transactions.value
142
- .fold(0, (acc, val) => val.height > acc ? val.height : acc);
127
+ final newHeight = transactions.fold(
128
+ 0, (int acc, val) => val.height > acc ? val.height : acc);
129
_height = newHeight > _height ? newHeight : _height;
130
}
131
}
lib/bitcoin/bitcoin_transaction_info.dart
+1
-1
@@ -66,7 +66,7 @@ class BitcoinTransactionInfo extends TransactionInfo {
66
String amountFormatted() => bitcoinAmountToString(amount: amount);
67
68
@override
69
- String fiatAmount() => '';
69
+ String fiatAmount() => '\$ 24.5';
70
71
Map<String, dynamic> toJson() {
72
final m = Map<String, dynamic>();
lib/bitcoin/bitcoin_wallet.dart
+124
-192
@@ -1,251 +1,184 @@
1
-import 'dart:async';
2
-import 'dart:convert';
1
import 'dart:typed_data';
4
-import 'package:cake_wallet/bitcoin/bitcoin_amount_format.dart';
5
-import 'package:cake_wallet/bitcoin/bitcoin_balance.dart';
6
-import 'package:cake_wallet/src/domain/common/sync_status.dart';
7
-import 'package:flutter/foundation.dart';
8
-import 'package:rxdart/rxdart.dart';
2
+import 'dart:convert';
3
+import 'package:mobx/mobx.dart';
4
import 'package:bip39/bip39.dart' as bip39;
5
+import 'package:flutter/foundation.dart';
6
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
7
import 'package:bitcoin_flutter/src/payments/index.dart' show PaymentData;
8
+import 'package:cake_wallet/src/domain/common/wallet_type.dart';
9
+import 'package:cake_wallet/bitcoin/bitcoin_transaction_history.dart';
10
+import 'package:cake_wallet/bitcoin/bitcoin_address_record.dart';
11
import 'package:cake_wallet/bitcoin/file.dart';
12
import 'package:cake_wallet/bitcoin/electrum.dart';
14
-import 'package:cake_wallet/bitcoin/bitcoin_transaction_history.dart';
15
-import 'package:cake_wallet/src/domain/common/pathForWallet.dart';
13
+import 'package:cake_wallet/bitcoin/bitcoin_balance.dart';
14
import 'package:cake_wallet/src/domain/common/node.dart';
17
-import 'package:cake_wallet/src/domain/common/pending_transaction.dart';
18
-import 'package:cake_wallet/src/domain/common/transaction_creation_credentials.dart';
19
-import 'package:cake_wallet/src/domain/common/transaction_history.dart';
20
-import 'package:cake_wallet/src/domain/common/wallet.dart';
21
-import 'package:cake_wallet/src/domain/common/wallet_type.dart';
15
+import 'package:cake_wallet/core/wallet_base.dart';
16
23
-class BitcoinWallet extends Wallet {
24
- BitcoinWallet(
25
- {@required this.hdwallet,
26
- @required this.eclient,
27
- @required this.path,
28
- @required String password,
29
- int accountIndex = 0,
30
- this.mnemonic})
31
- : _accountIndex = accountIndex,
32
- _password = password,
33
- _syncStatus = BehaviorSubject<SyncStatus>(),
34
- _onBalanceChange = BehaviorSubject<BitcoinBalance>(),
35
- _onAddressChange = BehaviorSubject<String>(),
36
- _onNameChange = BehaviorSubject<String>();
37
-
38
- @override
39
- Observable<BitcoinBalance> get onBalanceChange => _onBalanceChange.stream;
17
+part 'bitcoin_wallet.g.dart';
18
41
- @override
42
- Observable<SyncStatus> get syncStatus => _syncStatus.stream;
43
-
44
- @override
45
- String get name => path.split('/').last ?? '';
46
- @override
47
- String get address => hdwallet.address;
48
- String get xpub => hdwallet.base58;
19
+/* TODO: Save balance to a wallet file.
20
+ Load balance from the wallet file in `init` method.
21
+*/
22
50
- final String path;
51
- final bitcoin.HDWallet hdwallet;
52
- final ElectrumClient eclient;
53
- final String mnemonic;
54
- BitcoinTransactionHistory history;
55
-
56
- final BehaviorSubject<SyncStatus> _syncStatus;
57
- final BehaviorSubject<BitcoinBalance> _onBalanceChange;
58
- final BehaviorSubject<String> _onAddressChange;
59
- final BehaviorSubject<String> _onNameChange;
60
- BehaviorSubject<Object> _addressUpdatesSubject;
61
- StreamSubscription<Object> _addressUpdatesSubscription;
62
- final String _password;
63
- int _accountIndex;
23
+class BitcoinWallet = BitcoinWalletBase with _$BitcoinWallet;
24
65
- static Future<BitcoinWallet> load(
66
- {@required String name, @required String password}) async {
67
- final walletDirPath =
68
- await pathForWalletDir(name: name, type: WalletType.bitcoin);
69
- final walletPath = '$walletDirPath/$name';
70
- final walletJSONRaw = await read(path: walletPath, password: password);
71
- final jsoned = json.decode(walletJSONRaw) as Map<String, Object>;
72
- final mnemonic = jsoned['mnemonic'] as String;
25
+abstract class BitcoinWalletBase extends WalletBase<BitcoinBalance> with Store {
26
+ static BitcoinWallet fromJSON(
27
+ {@required String password,
28
+ @required String name,
29
+ @required String dirPath,
30
+ String jsonSource}) {
31
+ final data = json.decode(jsonSource) as Map;
32
+ final mnemonic = data['mnemonic'] as String;
33
final accountIndex =
74
- (jsoned['account_index'] == "null" || jsoned['account_index'] == null)
34
+ (data['account_index'] == "null" || data['account_index'] == null)
35
? 0
76
- : int.parse(jsoned['account_index'] as String);
36
+ : int.parse(data['account_index'] as String);
37
+ final _addresses = data['addresses'] as List;
38
+ final addresses = <BitcoinAddressRecord>[];
39
+ final balance = BitcoinBalance.fromJSON(data['balance'] as String) ??
40
+ BitcoinBalance(confirmed: 0, unconfirmed: 0);
41
+
42
+ _addresses?.forEach((Object el) {
43
+ if (el is String) {
44
+ addresses.add(BitcoinAddressRecord.fromJSON(el));
45
+ }
46
+ });
47
78
- return await build(
48
+ return BitcoinWalletBase.build(
49
+ dirPath: dirPath,
50
mnemonic: mnemonic,
51
password: password,
52
name: name,
82
- accountIndex: accountIndex);
53
+ accountIndex: accountIndex,
54
+ initialAddresses: addresses,
55
+ initialBalance: balance);
56
}
57
85
- static Future<BitcoinWallet> build(
58
+ static BitcoinWallet build(
59
{@required String mnemonic,
60
@required String password,
61
@required String name,
89
- int accountIndex = 0}) async {
90
- final hd = bitcoin.HDWallet.fromSeed(bip39.mnemonicToSeed(mnemonic),
91
- network: bitcoin.bitcoin);
92
- final walletDirPath =
93
- await pathForWalletDir(name: name, type: WalletType.bitcoin);
94
- final walletPath = '$walletDirPath/$name';
95
- final historyPath = '$walletDirPath/transactions.json';
62
+ @required String dirPath,
63
+ List<BitcoinAddressRecord> initialAddresses,
64
+ BitcoinBalance initialBalance,
65
+ int accountIndex = 0}) {
66
+ final walletPath = '$dirPath/$name';
67
final eclient = ElectrumClient();
97
- final wallet = BitcoinWallet(
98
- hdwallet: hd,
68
+ final history = BitcoinTransactionHistory(
69
+ eclient: eclient, dirPath: dirPath, password: password);
70
+
71
+ return BitcoinWallet._internal(
72
eclient: eclient,
73
path: walletPath,
74
+ name: name,
75
mnemonic: mnemonic,
76
password: password,
103
- accountIndex: accountIndex);
104
- final history = BitcoinTransactionHistory(
105
- eclient: eclient,
106
- path: historyPath,
107
- password: password,
108
- wallet: wallet);
109
- wallet.history = history;
110
- await history.init();
111
- await wallet.updateInfo();
112
-
113
- return wallet;
114
- }
115
-
116
- List<String> getAddresses() => _accountIndex == 0
117
- ? [address]
118
- : List<String>.generate(
119
- _accountIndex, (i) => _getAddress(hd: hdwallet, index: i));
120
-
121
- Future<String> newAddress() async {
122
- _accountIndex += 1;
123
- final address = _getAddress(hd: hdwallet, index: _accountIndex);
124
- await save();
125
-
126
- return address;
77
+ accountIndex: accountIndex,
78
+ initialAddresses: initialAddresses,
79
+ initialBalance: initialBalance,
80
+ transactionHistory: history);
81
}
82
129
- @override
130
- Future close() async {
131
- await _addressUpdatesSubscription?.cancel();
132
- }
83
+ BitcoinWalletBase._internal(
84
+ {@required this.eclient,
85
+ @required this.path,
86
+ @required String password,
87
+ @required this.name,
88
+ List<BitcoinAddressRecord> initialAddresses,
89
+ int accountIndex = 0,
90
+ this.transactionHistory,
91
+ this.mnemonic,
92
+ BitcoinBalance initialBalance}) {
93
+ balance = initialBalance ?? BitcoinBalance(confirmed: 0, unconfirmed: 0);
94
+ hd = bitcoin.HDWallet.fromSeed(bip39.mnemonicToSeed(mnemonic),
95
+ network: bitcoin.bitcoin);
96
+ addresses = initialAddresses != null
97
+ ? ObservableList<BitcoinAddressRecord>.of(initialAddresses)
98
+ : ObservableList<BitcoinAddressRecord>();
99
134
- @override
135
- Future connectToNode(
136
- {Node node, bool useSSL = false, bool isLightWallet = false}) async {
137
- try {
138
- // FIXME: Hardcoded server address
139
- // final uri = Uri.parse(node.uri);
140
- // https://electrum2.hodlister.co:50002
141
- await eclient.connect(host: 'electrum2.hodlister.co', port: 50002);
142
- _syncStatus.value = ConnectedSyncStatus();
143
- } catch (e) {
144
- print(e.toString());
145
- _syncStatus.value = FailedSyncStatus();
100
+ if (addresses.isEmpty) {
101
+ addresses.add(BitcoinAddressRecord(hd.address));
102
}
147
- }
103
149
- @override
150
- Future<PendingTransaction> createTransaction(
151
- TransactionCreationCredentials credentials) async {
152
- final txb = bitcoin.TransactionBuilder(network: bitcoin.bitcoin);
153
- final transactions = history.transactionsAll;
154
- history.transactionsAll.sort((q, w) => q.height.compareTo(w.height));
155
- final prevTx = transactions.first;
156
-
157
- txb.setVersion(1);
158
- txb.addInput(prevTx, 0);
159
- txb.addOutput('address', 112);
160
- txb.sign(vin: null, keyPair: null);
161
-
162
- final hex = txb.build().toHex();
163
-
164
- // broadcast transaction to electrum
165
- return null;
166
- }
104
+ address = addresses.first.address;
105
168
- @override
169
- Future<String> getAddress() async => address;
106
+ _password = password;
107
+ _accountIndex = accountIndex;
108
+ }
109
110
@override
172
- Future<int> getCurrentHeight() async => 0;
111
+ final BitcoinTransactionHistory transactionHistory;
112
+ final String path;
113
+ bitcoin.HDWallet hd;
114
+ final ElectrumClient eclient;
115
+ final String mnemonic;
116
+ int _accountIndex;
117
+ String _password;
118
119
@override
175
- Future<String> getFilename() async => path.split('/').last ?? '';
120
+ String name;
121
122
@override
178
- Future<String> getFullBalance() async =>
179
- bitcoinAmountToString(amount: _onBalanceChange.value.total);
123
+ @observable
124
+ String address;
125
126
@override
182
- TransactionHistory getHistory() => history;
127
+ @observable
128
+ BitcoinBalance balance;
129
130
@override
185
- Future<Map<String, String>> getKeys() async =>
186
- {'publicKey': hdwallet.pubKey, 'privateKey': hdwallet.privKey};
131
+ final type = WalletType.bitcoin;
132
188
- @override
189
- Future<String> getName() async => path.split('/').last ?? '';
133
+ ObservableList<BitcoinAddressRecord> addresses;
134
191
- @override
192
- Future<int> getNodeHeight() async => 0;
135
+ String get xpub => hd.base58;
136
194
- @override
195
- Future<String> getSeed() async => mnemonic;
137
+ Future<void> init() async {
138
+ await transactionHistory.init();
139
+ }
140
197
- @override
198
- WalletType getType() => WalletType.bitcoin;
141
+ Future<BitcoinAddressRecord> generateNewAddress({String label}) async {
142
+ _accountIndex += 1;
143
+ final address = BitcoinAddressRecord(
144
+ _getAddress(hd: hd, index: _accountIndex),
145
+ label: label);
146
+ addresses.add(address);
147
200
- @override
201
- Future<String> getUnlockedBalance() async =>
202
- bitcoinAmountToString(amount: _onBalanceChange.value.total);
148
+ await save();
149
204
- @override
205
- Future<bool> isConnected() async => eclient.isConnected;
150
+ return address;
151
+ }
152
207
- @override
208
- Observable<String> get onAddressChange => _onAddressChange.stream;
153
+ Future<void> updateAddress(String address, {String label}) async {
154
+ for (final addr in addresses) {
155
+ if (addr.address == address) {
156
+ addr.label = label;
157
+ await save();
158
+ break;
159
+ }
160
+ }
161
+ }
162
163
@override
211
- Observable<String> get onNameChange => _onNameChange.stream;
164
+ Future<void> startSync() async {}
165
166
@override
214
- Future rescan({int restoreHeight = 0}) {
215
- // TODO: implement rescan
216
- return null;
217
- }
167
+ Future<void> connectToNode({@required Node node}) async {}
168
169
@override
220
- Future startSync() async {
221
- _addressUpdatesSubject = eclient.addressUpdate(address: address);
222
- _addressUpdatesSubscription =
223
- _addressUpdatesSubject.listen((obj) => print('new obj: $obj'));
224
- _onBalanceChange.value = await fetchBalance();
225
- getHistory().update();
226
- }
170
+ Future<void> createTransaction(Object credentials) async {}
171
172
@override
229
- Future updateInfo() async {
230
- _onNameChange.value = await getName();
231
- // _addressUpdatesSubject = eclient.addressUpdate(address: address);
232
- // _addressUpdatesSubscription =
233
- // _addressUpdatesSubject.listen((obj) => print('new obj: $obj'));
234
- _onBalanceChange.value = BitcoinBalance(confirmed: 0, unconfirmed: 0);
235
- print(await getKeys());
236
- }
237
-
238
- Future<BitcoinBalance> fetchBalance() async {
239
- final balance = await _fetchBalances();
240
-
241
- return BitcoinBalance(
242
- confirmed: balance['confirmed'], unconfirmed: balance['unconfirmed']);
243
- }
173
+ Future<void> save() async =>
174
+ await write(path: path, password: _password, data: toJSON());
175
245
- Future<void> save() async => await write(
246
- path: path,
247
- password: _password,
248
- obj: {'mnemonic': mnemonic, 'account_index': _accountIndex.toString()});
176
+ String toJSON() => json.encode({
177
+ 'mnemonic': mnemonic,
178
+ 'account_index': _accountIndex.toString(),
179
+ 'addresses': addresses.map((addr) => addr.toJSON()).toList(),
180
+ 'balance': balance?.toJSON()
181
+ });
182
183
String _getAddress({bitcoin.HDWallet hd, int index}) => bitcoin
184
.P2PKH(
@@ -256,9 +189,8 @@ class BitcoinWallet extends Wallet {
189
190
Future<Map<String, int>> _fetchBalances() async {
191
final balances = await Future.wait(
259
- getAddresses().map((address) => eclient.getBalance(address: address)));
260
- final balance =
261
- balances.fold(Map<String, int>(), (Map<String, int> acc, val) {
192
+ addresses.map((record) => eclient.getBalance(address: record.address)));
193
+ final balance = balances.fold(<String, int>{}, (Map<String, int> acc, val) {
194
acc['confirmed'] =
195
(val['confirmed'] as int ?? 0) + (acc['confirmed'] ?? 0);
196
acc['unconfirmed'] =
lib/bitcoin/bitcoin_wallet.manager.dart
deleted
-59
@@ -1,59 +0,0 @@
1
-import 'dart:io';
2
-import 'package:bip39/bip39.dart' as bip39;
3
-import 'package:cake_wallet/bitcoin/bitcoin_wallet.dart';
4
-import 'package:cake_wallet/src/domain/common/pathForWallet.dart';
5
-import 'package:cake_wallet/src/domain/common/wallet.dart';
6
-import 'package:cake_wallet/src/domain/common/wallet_description.dart';
7
-import 'package:cake_wallet/src/domain/common/wallet_type.dart';
8
-import 'package:cake_wallet/src/domain/common/wallets_manager.dart';
9
-
10
-class BitcoinWalletManager extends WalletsManager {
11
- @override
12
- Future<Wallet> create(String name, String password, String language) async {
13
- final wallet = await BitcoinWallet.build(
14
- mnemonic: bip39.generateMnemonic(), password: password, name: name);
15
- await wallet.save();
16
-
17
- return wallet;
18
- }
19
-
20
- @override
21
- Future<bool> isWalletExit(String name) async =>
22
- File(await pathForWallet(name: name, type: WalletType.bitcoin))
23
- .existsSync();
24
-
25
- @override
26
- Future<Wallet> openWallet(String name, String password) async {
27
- return BitcoinWallet.load(
28
- name: name, password: password);
29
- }
30
-
31
- @override
32
- Future remove(WalletDescription wallet) async {
33
- final path = await pathForWalletDir(name: wallet.name, type: wallet.type);
34
- final f = File(path);
35
-
36
- if (!f.existsSync()) {
37
- return;
38
- }
39
-
40
- f.deleteSync();
41
- }
42
-
43
- @override
44
- Future<Wallet> restoreFromKeys(String name, String password, String language,
45
- int restoreHeight, String address, String viewKey, String spendKey) {
46
- // TODO: implement restoreFromKeys
47
- return null;
48
- }
49
-
50
- @override
51
- Future<Wallet> restoreFromSeed(
52
- String name, String password, String seed, int restoreHeight) async {
53
- final wallet = await BitcoinWallet.build(
54
- name: name, password: password, mnemonic: seed);
55
- await wallet.save();
56
-
57
- return wallet;
58
- }
59
-}
lib/bitcoin/bitcoin_wallet_creation_credentials.dart
new
+21
@@ -0,0 +1,21 @@
1
+import 'package:cake_wallet/core/wallet_credentials.dart';
2
+
3
+class BitcoinNewWalletCredentials extends WalletCredentials {
4
+ BitcoinNewWalletCredentials({String name}) : super(name: name);
5
+}
6
+
7
+class BitcoinRestoreWalletFromSeedCredentials extends WalletCredentials {
8
+ BitcoinRestoreWalletFromSeedCredentials(
9
+ {String name, String password, this.mnemonic})
10
+ : super(name: name, password: password);
11
+
12
+ final String mnemonic;
13
+}
14
+
15
+class BitcoinRestoreWalletFromWIFCredentials extends WalletCredentials {
16
+ BitcoinRestoreWalletFromWIFCredentials(
17
+ {String name, String password, this.wif})
18
+ : super(name: name, password: password);
19
+
20
+ final String wif;
21
+}
lib/bitcoin/bitcoin_wallet_service.dart
new
+79
@@ -0,0 +1,79 @@
1
+import 'dart:io';
2
+import 'dart:convert';
3
+import 'package:bip39/bip39.dart' as bip39;
4
+import 'package:cake_wallet/bitcoin/file.dart';
5
+import 'package:cake_wallet/bitcoin/bitcoin_wallet_creation_credentials.dart';
6
+import 'package:cake_wallet/core/wallet_service.dart';
7
+import 'package:cake_wallet/bitcoin/bitcoin_wallet.dart';
8
+import 'package:cake_wallet/src/domain/common/pathForWallet.dart';
9
+import 'package:cake_wallet/src/domain/common/wallet_type.dart';
10
+
11
+class BitcoinWalletService extends WalletService<
12
+ BitcoinNewWalletCredentials,
13
+ BitcoinRestoreWalletFromSeedCredentials,
14
+ BitcoinRestoreWalletFromWIFCredentials> {
15
+ @override
16
+ Future<BitcoinWallet> create(BitcoinNewWalletCredentials credentials) async {
17
+ final dirPath = await pathForWalletDir(
18
+ type: WalletType.bitcoin, name: credentials.name);
19
+ final wallet = BitcoinWalletBase.build(
20
+ dirPath: dirPath,
21
+ mnemonic: bip39.generateMnemonic(),
22
+ password: credentials.password,
23
+ name: credentials.name);
24
+ await wallet.save();
25
+ await wallet.init();
26
+
27
+ return wallet;
28
+ }
29
+
30
+ @override
31
+ Future<bool> isWalletExit(String name) async =>
32
+ File(await pathForWallet(name: name, type: WalletType.bitcoin))
33
+ .existsSync();
34
+
35
+ @override
36
+ Future<BitcoinWallet> openWallet(String name, String password) async {
37
+ final walletDirPath =
38
+ await pathForWalletDir(name: name, type: WalletType.bitcoin);
39
+ final walletPath = '$walletDirPath/$name';
40
+ final walletJSONRaw = await read(path: walletPath, password: password);
41
+ final wallet = BitcoinWalletBase.fromJSON(
42
+ password: password,
43
+ name: name,
44
+ dirPath: walletDirPath,
45
+ jsonSource: walletJSONRaw);
46
+ await wallet.init();
47
+
48
+ return wallet;
49
+ }
50
+
51
+ @override
52
+ Future<void> remove(String wallet) {
53
+ // TODO: implement remove
54
+ throw UnimplementedError();
55
+ }
56
+
57
+ @override
58
+ Future<BitcoinWallet> restoreFromKeys(
59
+ BitcoinRestoreWalletFromWIFCredentials credentials) async {
60
+ // TODO: implement restoreFromKeys
61
+ throw UnimplementedError();
62
+ }
63
+
64
+ @override
65
+ Future<BitcoinWallet> restoreFromSeed(
66
+ BitcoinRestoreWalletFromSeedCredentials credentials) async {
67
+ final dirPath = await pathForWalletDir(
68
+ type: WalletType.bitcoin, name: credentials.name);
69
+ final wallet = BitcoinWalletBase.build(
70
+ dirPath: dirPath,
71
+ name: credentials.name,
72
+ password: credentials.password,
73
+ mnemonic: credentials.mnemonic);
74
+ await wallet.save();
75
+ await wallet.init();
76
+
77
+ return wallet;
78
+ }
79
+}
lib/bitcoin/file.dart
+2
-3
@@ -7,12 +7,11 @@ import 'package:flutter/foundation.dart';
7
Future<void> write(
8
{@required String path,
9
@required String password,
10
- @required Map<String, String> obj}) async {
11
- final jsoned = json.encode(obj);
10
+ @required String data}) async {
11
final keys = extractKeys(password);
12
final key = encrypt.Key.fromBase64(keys.first);
13
final iv = encrypt.IV.fromBase64(keys.last);
15
- final encrypted = await encode(key: key, iv: iv, data: jsoned);
14
+ final encrypted = await encode(key: key, iv: iv, data: data);
15
final f = File(path);
16
f.writeAsStringSync(encrypted);
17
}
lib/core/AddressLabelValidator.dart
new
+12
@@ -0,0 +1,12 @@
1
+import 'package:cake_wallet/core/validator.dart';
2
+import 'package:cake_wallet/generated/i18n.dart';
3
+import 'package:cake_wallet/src/domain/common/wallet_type.dart';
4
+
5
+class AddressLabelValidator extends TextValidator {
6
+ AddressLabelValidator({WalletType type})
7
+ : super(
8
+ errorMessage: S.current.error_text_subaddress_name,
9
+ pattern: '''^[^`,'"]{1,20}\$''',
10
+ minLength: 1,
11
+ maxLength: 20);
12
+}
lib/core/amount_validator.dart
new
+24
@@ -0,0 +1,24 @@
1
+import 'package:cake_wallet/core/validator.dart';
2
+import 'package:cake_wallet/generated/i18n.dart';
3
+import 'package:cake_wallet/src/domain/common/wallet_type.dart';
4
+
5
+class AmountValidator extends TextValidator {
6
+ AmountValidator({WalletType type})
7
+ : super(
8
+ errorMessage: S.current.error_text_amount,
9
+ pattern: _pattern(type),
10
+ minLength: 0,
11
+ maxLength: 0);
12
+
13
+ static String _pattern(WalletType type) {
14
+ switch (type) {
15
+ case WalletType.monero:
16
+ return '^([0-9]+([.][0-9]{0,12})?|[.][0-9]{1,12})\$';
17
+ case WalletType.bitcoin:
18
+ // FIXME: Incorrect pattern for bitcoin
19
+ return '^([0-9]+([.][0-9]{0,12})?|[.][0-9]{1,12})\$';
20
+ default:
21
+ return '';
22
+ }
23
+ }
24
+}
lib/core/app_service.dart
deleted
-16
@@ -1,16 +0,0 @@
1
-import 'package:mobx/mobx.dart';
2
-import 'package:cake_wallet/core/auth_service.dart';
3
-import 'package:cake_wallet/core/wallet_base.dart';
4
-import 'package:cake_wallet/core/wallet_creation_service.dart';
5
-
6
-part 'app_service.g.dart';
7
-
8
-class AppService = AppServiceBase with _$AppService;
9
-
10
-abstract class AppServiceBase with Store {
11
- AppServiceBase({this.walletCreationService, this.authService, this.wallet});
12
-
13
- WalletCreationService walletCreationService;
14
- AuthService authService;
15
- WalletBase wallet;
16
-}
\ No newline at end of file
lib/core/auth_service.dart
+31
-11
@@ -1,21 +1,41 @@
1
import 'package:flutter/foundation.dart';
2
import 'package:mobx/mobx.dart';
3
-import 'package:cake_wallet/core/setup_pin_code_state.dart';
3
+import 'package:flutter_secure_storage/flutter_secure_storage.dart';
4
+import 'package:shared_preferences/shared_preferences.dart';
5
+import 'package:cake_wallet/src/domain/common/secret_store_key.dart';
6
+import 'package:cake_wallet/src/domain/common/encrypt.dart';
7
5
-part 'auth_service.g.dart';
8
+class AuthService with Store {
9
+ AuthService({this.secureStorage, this.sharedPreferences});
10
7
-class AuthService = AuthServiceBase with _$AuthService;
11
+ final FlutterSecureStorage secureStorage;
12
+ final SharedPreferences sharedPreferences;
13
9
-abstract class AuthServiceBase with Store {
10
- @observable
11
- SetupPinCodeState setupPinCodeState;
14
+ Future setPassword(String password) async {
15
+ final key = generateStoreKeyFor(key: SecretStoreKey.pinCodePassword);
16
+ final encodedPassword = encodedPinCode(pin: password);
17
+ await secureStorage.write(key: key, value: encodedPassword);
18
+ }
19
+
20
+ Future<bool> canAuthenticate() async {
21
+ final key = generateStoreKeyFor(key: SecretStoreKey.pinCodePassword);
22
+ final walletName = sharedPreferences.getString('current_wallet_name') ?? '';
23
+ var password = '';
24
13
- Future<void> setupPinCode({@required String pin}) async {}
25
+ try {
26
+ password = await secureStorage.read(key: key);
27
+ } catch (e) {
28
+ print(e);
29
+ }
30
15
- Future<bool> authenticate({@required String pin}) async {
16
- return false;
31
+ return walletName.isNotEmpty && password.isNotEmpty;
32
}
33
19
- void resetSetupPinCodeState() =>
20
- setupPinCodeState = InitialSetupPinCodeState();
34
+ Future<bool> authenticate(String pin) async {
35
+ final key = generateStoreKeyFor(key: SecretStoreKey.pinCodePassword);
36
+ final encodedPin = await secureStorage.read(key: key);
37
+ final decodedPin = decodedPinCode(pin: encodedPin);
38
+
39
+ return decodedPin == pin;
40
+ }
41
}
lib/core/auth_state.dart
renamed
lib/core/bitcoin_transaction_history.dart
deleted
-121
@@ -1,121 +0,0 @@
1
-import 'dart:convert';
2
-import 'package:flutter/foundation.dart';
3
-import 'package:mobx/mobx.dart';
4
-import 'package:cake_wallet/core/transaction_history.dart';
5
-import 'package:cake_wallet/core/bitcoin_wallet.dart';
6
-import 'package:cake_wallet/bitcoin/bitcoin_transaction_info.dart';
7
-import 'package:cake_wallet/bitcoin/electrum.dart';
8
-import 'package:cake_wallet/src/domain/common/transaction_info.dart';
9
-import 'package:cake_wallet/bitcoin/file.dart';
10
-
11
-part 'bitcoin_transaction_history.g.dart';
12
-
13
-// TODO: Think about another transaction store for bitcoin transaction history..
14
-
15
-const _transactionsHistoryFileName = 'transactions.json';
16
-
17
-class BitcoinTransactionHistory = BitcoinTransactionHistoryBase
18
- with _$BitcoinTransactionHistory;
19
-
20
-abstract class BitcoinTransactionHistoryBase
21
- extends TranasctionHistoryBase<BitcoinTransactionInfo> with Store {
22
- BitcoinTransactionHistoryBase(
23
- {this.eclient, String dirPath, @required String password})
24
- : path = '$dirPath/$_transactionsHistoryFileName',
25
- _password = password,
26
- _height = 0;
27
-
28
- BitcoinWallet wallet;
29
- final ElectrumClient eclient;
30
- final String path;
31
- final String _password;
32
- int _height;
33
-
34
- Future<void> init() async {
35
- // TODO: throw exeption if wallet is null;
36
- final info = await _read();
37
- _height = (info['height'] as int) ?? _height;
38
- transactions = info['transactions'] as List<BitcoinTransactionInfo>;
39
- }
40
-
41
- @override
42
- Future update() async {
43
- await super.update();
44
- _updateHeight();
45
- }
46
-
47
- @override
48
- Future<List<BitcoinTransactionInfo>> fetchTransactions() async {
49
- final addresses = wallet.getAddresses();
50
- final histories =
51
- addresses.map((address) => eclient.getHistory(address: address));
52
- final _historiesWithDetails = await Future.wait(histories)
53
- .then((histories) => histories
54
- .map((h) => h.where((tx) => (tx['height'] as int) > _height))
55
- .expand((i) => i)
56
- .toList())
57
- .then((histories) => histories.map((tx) => fetchTransactionInfo(
58
- hash: tx['tx_hash'] as String, height: tx['height'] as int)));
59
- final historiesWithDetails = await Future.wait(_historiesWithDetails);
60
-
61
- return historiesWithDetails
62
- .map((info) => BitcoinTransactionInfo.fromHexAndHeader(
63
- info['raw'] as String, info['header'] as Map<String, Object>,
64
- addresses: addresses))
65
- .toList();
66
- }
67
-
68
- Future<Map<String, Object>> fetchTransactionInfo(
69
- {@required String hash, @required int height}) async {
70
- final rawFetching = eclient.getTransactionRaw(hash: hash);
71
- final headerFetching = eclient.getHeader(height: height);
72
- final result = await Future.wait([rawFetching, headerFetching]);
73
- final raw = result.first as String;
74
- final header = result[1] as Map<String, Object>;
75
-
76
- return {'raw': raw, 'header': header};
77
- }
78
-
79
- Future<void> add(List<BitcoinTransactionInfo> transactions) async {
80
- this.transactions.addAll(transactions);
81
- await save();
82
- }
83
-
84
- Future<void> addOne(BitcoinTransactionInfo tx) async {
85
- transactions.add(tx);
86
- await save();
87
- }
88
-
89
- Future<void> save() async => writeData(
90
- path: path,
91
- password: _password,
92
- data: json.encode({'height': _height, 'transactions': transactions}));
93
-
94
- Future<Map<String, Object>> _read() async {
95
- try {
96
- final content = await read(path: path, password: _password);
97
- final jsoned = json.decode(content) as Map<String, Object>;
98
- final height = jsoned['height'] as int;
99
- final transactions = (jsoned['transactions'] as List<dynamic>)
100
- .map((dynamic row) {
101
- if (row is Map<String, Object>) {
102
- return BitcoinTransactionInfo.fromJson(row);
103
- }
104
-
105
- return null;
106
- })
107
- .where((el) => el != null)
108
- .toList();
109
-
110
- return {'transactions': transactions, 'height': height};
111
- } catch (_) {
112
- return {'transactions': <TransactionInfo>[], 'height': 0};
113
- }
114
- }
115
-
116
- void _updateHeight() {
117
- final newHeight = transactions.fold(
118
- 0, (int acc, val) => val.height > acc ? val.height : acc);
119
- _height = newHeight > _height ? newHeight : _height;
120
- }
121
-}
lib/core/bitcoin_wallet.dart
deleted
-149
@@ -1,149 +0,0 @@
1
-import 'dart:convert';
2
-import 'dart:typed_data';
3
-import 'package:cake_wallet/core/bitcoin_transaction_history.dart';
4
-import 'package:cake_wallet/core/transaction_history.dart';
5
-import 'package:mobx/mobx.dart';
6
-import 'package:bip39/bip39.dart' as bip39;
7
-import 'package:flutter/foundation.dart';
8
-import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
9
-import 'package:bitcoin_flutter/src/payments/index.dart' show PaymentData;
10
-import 'package:cake_wallet/bitcoin/file.dart';
11
-import 'package:cake_wallet/src/domain/common/pathForWallet.dart';
12
-import 'package:cake_wallet/src/domain/common/wallet_type.dart';
13
-import 'package:cake_wallet/bitcoin/electrum.dart';
14
-import 'package:cake_wallet/bitcoin/bitcoin_balance.dart';
15
-import 'package:cake_wallet/src/domain/common/node.dart';
16
-import 'wallet_base.dart';
17
-
18
-part 'bitcoin_wallet.g.dart';
19
-
20
-/* TODO: Save balance to a wallet file.
21
- Load balance from the wallet file in `init` method.
22
-*/
23
-
24
-class BitcoinWallet = BitcoinWalletBase with _$BitcoinWallet;
25
-
26
-abstract class BitcoinWalletBase extends WalletBase<BitcoinBalance> with Store {
27
- static Future<BitcoinWalletBase> load(
28
- {@required String name, @required String password}) async {
29
- final walletDirPath =
30
- await pathForWalletDir(name: name, type: WalletType.bitcoin);
31
- final walletPath = '$walletDirPath/$name';
32
- final walletJSONRaw = await read(path: walletPath, password: password);
33
- final jsoned = json.decode(walletJSONRaw) as Map<String, Object>;
34
- final mnemonic = jsoned['mnemonic'] as String;
35
- final accountIndex =
36
- (jsoned['account_index'] == "null" || jsoned['account_index'] == null)
37
- ? 0
38
- : int.parse(jsoned['account_index'] as String);
39
-
40
- return BitcoinWalletBase.build(
41
- mnemonic: mnemonic,
42
- password: password,
43
- name: name,
44
- accountIndex: accountIndex);
45
- }
46
-
47
- factory BitcoinWalletBase.build(
48
- {@required String mnemonic,
49
- @required String password,
50
- @required String name,
51
- @required String dirPath,
52
- int accountIndex = 0}) {
53
- final walletPath = '$dirPath/$name';
54
- final eclient = ElectrumClient();
55
- final history = BitcoinTransactionHistory(
56
- eclient: eclient, dirPath: dirPath, password: password);
57
-
58
- return BitcoinWallet._internal(
59
- eclient: eclient,
60
- path: walletPath,
61
- mnemonic: mnemonic,
62
- password: password,
63
- accountIndex: accountIndex,
64
- transactionHistory: history);
65
- }
66
-
67
- BitcoinWalletBase._internal(
68
- {@required this.eclient,
69
- @required this.path,
70
- @required String password,
71
- int accountIndex = 0,
72
- this.transactionHistory,
73
- this.mnemonic}) {
74
- hd = bitcoin.HDWallet.fromSeed(bip39.mnemonicToSeed(mnemonic),
75
- network: bitcoin.bitcoin);
76
- _password = password;
77
- _accountIndex = accountIndex;
78
- }
79
-
80
- final BitcoinTransactionHistory transactionHistory;
81
- final String path;
82
- bitcoin.HDWallet hd;
83
- final ElectrumClient eclient;
84
- final String mnemonic;
85
- int _accountIndex;
86
- String _password;
87
-
88
- @override
89
- String get name => path.split('/').last ?? '';
90
-
91
- @override
92
- String get filename => hd.address;
93
-
94
- String get xpub => hd.base58;
95
-
96
- List<String> getAddresses() => _accountIndex == 0
97
- ? [address]
98
- : List<String>.generate(
99
- _accountIndex, (i) => _getAddress(hd: hd, index: i));
100
-
101
- Future<void> init() async {
102
- await transactionHistory.init();
103
- }
104
-
105
- Future<String> newAddress() async {
106
- _accountIndex += 1;
107
- final address = _getAddress(hd: hd, index: _accountIndex);
108
- await save();
109
-
110
- return address;
111
- }
112
-
113
- @override
114
- Future<void> startSync() async {}
115
-
116
- @override
117
- Future<void> connectToNode({@required Node node}) async {}
118
-
119
- @override
120
- Future<void> createTransaction(Object credentials) async {}
121
-
122
- @override
123
- Future<void> save() async => await write(
124
- path: path,
125
- password: _password,
126
- obj: {'mnemonic': mnemonic, 'account_index': _accountIndex.toString()});
127
-
128
- String _getAddress({bitcoin.HDWallet hd, int index}) => bitcoin
129
- .P2PKH(
130
- data: PaymentData(
131
- pubkey: Uint8List.fromList(hd.derive(index).pubKey.codeUnits)))
132
- .data
133
- .address;
134
-
135
- Future<Map<String, int>> _fetchBalances() async {
136
- final balances = await Future.wait(
137
- getAddresses().map((address) => eclient.getBalance(address: address)));
138
- final balance = balances.fold(<String, int>{}, (Map<String, int> acc, val) {
139
- acc['confirmed'] =
140
- (val['confirmed'] as int ?? 0) + (acc['confirmed'] ?? 0);
141
- acc['unconfirmed'] =
142
- (val['unconfirmed'] as int ?? 0) + (acc['unconfirmed'] ?? 0);
143
-
144
- return acc;
145
- });
146
-
147
- return balance;
148
- }
149
-}
lib/core/bitcoin_wallet_list_service.dart
deleted
-103
@@ -1,103 +0,0 @@
1
-import 'dart:io';
2
-import 'package:bip39/bip39.dart' as bip39;
3
-import 'package:cake_wallet/bitcoin/bitcoin_wallet.dart';
4
-import 'package:cake_wallet/core/wallet_credentials.dart';
5
-import 'package:cake_wallet/core/wallet_list_service.dart';
6
-import 'package:cake_wallet/core/bitcoin_wallet.dart';
7
-import 'package:cake_wallet/src/domain/common/pathForWallet.dart';
8
-import 'package:cake_wallet/src/domain/common/wallet.dart';
9
-import 'package:cake_wallet/src/domain/common/wallet_description.dart';
10
-import 'package:cake_wallet/src/domain/common/wallet_type.dart';
11
-import 'package:cake_wallet/src/domain/common/wallets_manager.dart';
12
-/*
13
-*
14
-* BitcoinRestoreWalletFromSeedCredentials
15
-*
16
-* */
17
-
18
-class BitcoinNewWalletCredentials extends WalletCredentials {}
19
-
20
-/*
21
-*
22
-* BitcoinRestoreWalletFromSeedCredentials
23
-*
24
-* */
25
-
26
-class BitcoinRestoreWalletFromSeedCredentials extends WalletCredentials {
27
- const BitcoinRestoreWalletFromSeedCredentials(
28
- {String name, String password, this.mnemonic})
29
- : super(name: name, password: password);
30
-
31
- final String mnemonic;
32
-}
33
-
34
-/*
35
-*
36
-* BitcoinRestoreWalletFromWIFCredentials
37
-*
38
-* */
39
-
40
-class BitcoinRestoreWalletFromWIFCredentials extends WalletCredentials {
41
- const BitcoinRestoreWalletFromWIFCredentials(
42
- {String name, String password, this.wif})
43
- : super(name: name, password: password);
44
-
45
- final String wif;
46
-}
47
-
48
-/*
49
-*
50
-* BitcoinWalletListService
51
-*
52
-* */
53
-
54
-class BitcoinWalletListService extends WalletListService<
55
- BitcoinNewWalletCredentials,
56
- BitcoinRestoreWalletFromSeedCredentials,
57
- BitcoinRestoreWalletFromWIFCredentials> {
58
- @override
59
- Future<void> create(BitcoinNewWalletCredentials credentials) async {
60
- final wallet = await BitcoinWalletBase.build(
61
- mnemonic: bip39.generateMnemonic(),
62
- password: credentials.password,
63
- name: credentials.name);
64
- await wallet.save();
65
-
66
- return wallet;
67
- }
68
-
69
- @override
70
- Future<bool> isWalletExit(String name) async =>
71
- File(await pathForWallet(name: name, type: WalletType.bitcoin))
72
- .existsSync();
73
-
74
- @override
75
- Future<void> openWallet(String name, String password) async {
76
- // TODO: implement openWallet
77
- throw UnimplementedError();
78
- }
79
-
80
- Future<void> remove(String wallet) {
81
- // TODO: implement remove
82
- throw UnimplementedError();
83
- }
84
-
85
- @override
86
- Future<void> restoreFromKeys(
87
- BitcoinRestoreWalletFromWIFCredentials credentials) async {
88
- // TODO: implement restoreFromKeys
89
- throw UnimplementedError();
90
- }
91
-
92
- @override
93
- Future<void> restoreFromSeed(
94
- BitcoinRestoreWalletFromSeedCredentials credentials) async {
95
- final wallet = await BitcoinWalletBase.build(
96
- name: credentials.name,
97
- password: credentials.password,
98
- mnemonic: credentials.mnemonic);
99
- await wallet.save();
100
-
101
- return wallet;
102
- }
103
-}
lib/core/generate_wallet_password.dart
new
+12
@@ -0,0 +1,12 @@
1
+import 'package:uuid/uuid.dart';
2
+import 'package:cake_wallet/bitcoin/key.dart';
3
+import 'package:cake_wallet/src/domain/common/wallet_type.dart';
4
+
5
+String generateWalletPassword(WalletType type) {
6
+ switch (type) {
7
+ case WalletType.bitcoin:
8
+ return generateKey();
9
+ default:
10
+ return Uuid().v4();
11
+ }
12
+}
lib/core/mnemonic_length.dart
new
+17
@@ -0,0 +1,17 @@
1
+import 'package:cake_wallet/src/domain/common/wallet_type.dart';
2
+
3
+const bitcoinMnemonicLength = 12;
4
+const moneroMnemonicLength = 25;
5
+
6
+int mnemonicLength(WalletType type) {
7
+ // TODO: need to have only one place for get(set) mnemonic string lenth;
8
+
9
+ switch (type) {
10
+ case WalletType.monero:
11
+ return moneroMnemonicLength;
12
+ case WalletType.bitcoin:
13
+ return bitcoinMnemonicLength;
14
+ default:
15
+ return 0;
16
+ }
17
+}
\ No newline at end of file
lib/core/seed_validator.dart
new
+73
@@ -0,0 +1,73 @@
1
+import 'package:bip39/src/wordlists/english.dart' as bitcoin_english;
2
+import 'package:cake_wallet/core/validator.dart';
3
+import 'package:cake_wallet/src/domain/common/mnemonic_item.dart';
4
+import 'package:cake_wallet/src/domain/common/wallet_type.dart';
5
+import 'package:cake_wallet/src/domain/monero/mnemonics/chinese_simplified.dart';
6
+import 'package:cake_wallet/src/domain/monero/mnemonics/dutch.dart';
7
+import 'package:cake_wallet/src/domain/monero/mnemonics/english.dart';
8
+import 'package:cake_wallet/src/domain/monero/mnemonics/german.dart';
9
+import 'package:cake_wallet/src/domain/monero/mnemonics/japanese.dart';
10
+import 'package:cake_wallet/src/domain/monero/mnemonics/portuguese.dart';
11
+import 'package:cake_wallet/src/domain/monero/mnemonics/russian.dart';
12
+import 'package:cake_wallet/src/domain/monero/mnemonics/spanish.dart';
13
+
14
+class SeedValidator extends Validator<MnemonicItem> {
15
+ SeedValidator({this.type, this.language})
16
+ : _words = getWordList(type: type, language: language);
17
+
18
+ final WalletType type;
19
+ final String language;
20
+ final List<String> _words;
21
+
22
+ static List<String> getWordList({WalletType type, String language}) {
23
+ switch (type) {
24
+ case WalletType.bitcoin:
25
+ return getBitcoinWordList(language);
26
+ case WalletType.monero:
27
+ return getMoneroWordList(language);
28
+ default:
29
+ return [];
30
+ }
31
+ }
32
+
33
+ static List<String> getMoneroWordList(String language) {
34
+ // FIXME: Unnamed constants; Need to be sure that string are in same case;
35
+
36
+ switch (language) {
37
+ case 'English':
38
+ return EnglishMnemonics.words;
39
+ break;
40
+ case 'Chinese (simplified)':
41
+ return ChineseSimplifiedMnemonics.words;
42
+ break;
43
+ case 'Dutch':
44
+ return DutchMnemonics.words;
45
+ break;
46
+ case 'German':
47
+ return GermanMnemonics.words;
48
+ break;
49
+ case 'Japanese':
50
+ return JapaneseMnemonics.words;
51
+ break;
52
+ case 'Portuguese':
53
+ return PortugueseMnemonics.words;
54
+ break;
55
+ case 'Russian':
56
+ return RussianMnemonics.words;
57
+ break;
58
+ case 'Spanish':
59
+ return SpanishMnemonics.words;
60
+ break;
61
+ default:
62
+ return EnglishMnemonics.words;
63
+ }
64
+ }
65
+
66
+ static List<String> getBitcoinWordList(String language) {
67
+ assert(language.toLowerCase() == 'english');
68
+ return bitcoin_english.WORDLIST;
69
+ }
70
+
71
+ @override
72
+ bool isValid(MnemonicItem value) => _words.contains(value.text);
73
+}
lib/core/setup_pin_code_state.dart
deleted
-15
@@ -1,15 +0,0 @@
1
-import 'package:flutter/foundation.dart';
2
-
3
-abstract class SetupPinCodeState {}
4
-
5
-class InitialSetupPinCodeState extends SetupPinCodeState {}
6
-
7
-class SetupPinCodeInProgress extends SetupPinCodeState {}
8
-
9
-class SetupPinCodeFinishedSuccessfully extends SetupPinCodeState {}
10
-
11
-class SetupPinCodeFinishedFailure extends SetupPinCodeState {
12
- SetupPinCodeFinishedFailure({@required this.error});
13
-
14
- final String error;
15
-}
\ No newline at end of file
lib/core/transaction_history.dart
+5
-4
@@ -1,10 +1,11 @@
1
import 'package:mobx/mobx.dart';
2
+import 'package:cake_wallet/src/domain/common/transaction_info.dart';
3
3
-abstract class TranasctionHistoryBase<TransactionType> {
4
- TranasctionHistoryBase() : _isUpdating = false;
4
+abstract class TransactionHistoryBase<TransactionType extends TransactionInfo> {
5
+ TransactionHistoryBase() : _isUpdating = false;
6
7
@observable
7
- List<TransactionType> transactions;
8
+ ObservableList<TransactionType> transactions;
9
10
bool _isUpdating;
11
@@ -15,7 +16,7 @@ abstract class TranasctionHistoryBase<TransactionType> {
16
17
try {
18
_isUpdating = false;
18
- transactions = await fetchTransactions();
19
+ transactions.addAll(await fetchTransactions());
20
_isUpdating = true;
21
} catch (e) {
22
_isUpdating = false;
lib/core/validator.dart
+39
@@ -0,0 +1,39 @@
1
+import 'package:flutter/foundation.dart';
2
+
3
+abstract class Validator<T> {
4
+ Validator({@required this.errorMessage});
5
+
6
+ final String errorMessage;
7
+
8
+ bool isValid(T value);
9
+
10
+ String call(T value) => !isValid(value) ? errorMessage : null;
11
+}
12
+
13
+class TextValidator extends Validator<String> {
14
+ TextValidator(
15
+ {this.minLength, this.maxLength, this.pattern, String errorMessage})
16
+ : super(errorMessage: errorMessage);
17
+
18
+ final int minLength;
19
+ final int maxLength;
20
+ String pattern;
21
+
22
+ @override
23
+ bool isValid(String value) {
24
+ if (value == null || value.isEmpty) {
25
+ return true;
26
+ }
27
+
28
+ return value.length > minLength &&
29
+ (maxLength > 0 ? (value.length <= maxLength) : true) &&
30
+ (pattern != null ? match(value) : true);
31
+ }
32
+
33
+ bool match(String value) => RegExp(pattern).hasMatch(value);
34
+}
35
+
36
+class WalletNameValidator extends TextValidator {
37
+ WalletNameValidator()
38
+ : super(minLength: 1, maxLength: 15, pattern: '^[a-zA-Z0-9_]\$');
39
+}
lib/core/wallet_base.dart
+10
-6
@@ -1,20 +1,24 @@
1
import 'package:flutter/foundation.dart';
2
-import 'package:mobx/mobx.dart';
2
+import 'package:cake_wallet/core/transaction_history.dart';
3
import 'package:cake_wallet/src/domain/common/node.dart';
4
+import 'package:cake_wallet/src/domain/common/wallet_type.dart';
5
6
abstract class WalletBase<BalaceType> {
6
- String get name;
7
+ WalletType type;
8
8
- String get filename;
9
+ String get name;
10
10
- @observable
11
String address;
12
13
- @observable
13
BalaceType balance;
14
15
+ TransactionHistoryBase transactionHistory;
16
+
17
Future<void> connectToNode({@required Node node});
18
+
19
Future<void> startSync();
20
+
21
Future<void> createTransaction(Object credentials);
22
+
23
Future<void> save();
20
-}
\ No newline at end of file
24
+}
lib/core/wallet_creation_service.dart
+63
-37
@@ -1,34 +1,47 @@
1
-import 'package:cake_wallet/core/wallet_creation_state.dart';
1
import 'package:flutter/foundation.dart';
3
-import 'package:mobx/mobx.dart';
2
+import 'package:flutter_secure_storage/flutter_secure_storage.dart';
3
+import 'package:shared_preferences/shared_preferences.dart';
4
+import 'package:cake_wallet/core/generate_wallet_password.dart';
5
+import 'package:cake_wallet/store/app_store.dart';
6
import 'package:cake_wallet/core/wallet_credentials.dart';
5
-import 'package:cake_wallet/core/bitcoin_wallet_list_service.dart';
6
-import 'package:cake_wallet/core/monero_wallet_list_service.dart';
7
-import 'package:cake_wallet/core/wallet_list_service.dart';
7
+import 'package:cake_wallet/bitcoin/bitcoin_wallet_service.dart';
8
+import 'package:cake_wallet/monero/monero_wallet_service.dart';
9
+import 'package:cake_wallet/core/wallet_service.dart';
10
import 'package:cake_wallet/src/domain/common/wallet_type.dart';
11
+import 'package:cake_wallet/src/domain/common/secret_store_key.dart';
12
+import 'package:cake_wallet/src/domain/common/encrypt.dart';
13
10
-part 'wallet_creation_service.g.dart';
11
-
12
-class WalletCreationService = WalletCreationServiceBase
13
- with _$WalletCreationService;
14
-
15
-abstract class WalletCreationServiceBase with Store {
16
- @observable
17
- WalletCreationState state;
14
+class WalletCreationService {
15
+ WalletCreationService(
16
+ {WalletType initialType,
17
+ this.appStore,
18
+ this.secureStorage,
19
+ this.sharedPreferences})
20
+ : type = initialType {
21
+ if (type != null) {
22
+ changeWalletType(type: type);
23
+ }
24
+ }
25
26
WalletType type;
27
+ final AppStore appStore;
28
+ final FlutterSecureStorage secureStorage;
29
+ final SharedPreferences sharedPreferences;
30
21
- WalletListService _service;
31
+// final WalletService walletService;
32
+// final Box<WalletInfo> walletInfoSource;
33
+
34
+ WalletService _service;
35
36
void changeWalletType({@required WalletType type}) {
37
this.type = type;
38
39
switch (type) {
40
case WalletType.monero:
28
- _service = MoneroWalletListService();
41
+ _service = MoneroWalletService();
42
break;
43
case WalletType.bitcoin:
31
- _service = BitcoinWalletListService();
44
+ _service = BitcoinWalletService();
45
break;
46
default:
47
break;
@@ -36,32 +49,45 @@ abstract class WalletCreationServiceBase with Store {
49
}
50
51
Future<void> create(WalletCredentials credentials) async {
39
- try {
40
- state = WalletCreating();
41
- await _service.create(credentials);
42
- state = WalletCreatedSuccessfully();
43
- } catch (e) {
44
- state = WalletCreationFailure(error: e.toString());
45
- }
52
+ final password = generateWalletPassword(type);
53
+ credentials.password = password;
54
+ await saveWalletPassword(password: password, walletName: credentials.name);
55
+ final wallet = await _service.create(credentials);
56
+ appStore.wallet = wallet;
57
+ appStore.authenticationStore.allowed();
58
}
59
60
Future<void> restoreFromKeys(WalletCredentials credentials) async {
49
- try {
50
- state = WalletCreating();
51
- await _service.restoreFromKeys(credentials);
52
- state = WalletCreatedSuccessfully();
53
- } catch (e) {
54
- state = WalletCreationFailure(error: e.toString());
55
- }
61
+ final password = generateWalletPassword(type);
62
+ credentials.password = password;
63
+ await saveWalletPassword(password: password, walletName: credentials.name);
64
+ final wallet = await _service.restoreFromKeys(credentials);
65
+ appStore.wallet = wallet;
66
+ appStore.authenticationStore.allowed();
67
}
68
69
Future<void> restoreFromSeed(WalletCredentials credentials) async {
59
- try {
60
- state = WalletCreating();
61
- await _service.restoreFromSeed(credentials);
62
- state = WalletCreatedSuccessfully();
63
- } catch (e) {
64
- state = WalletCreationFailure(error: e.toString());
65
- }
70
+ final password = generateWalletPassword(type);
71
+ credentials.password = password;
72
+ await saveWalletPassword(password: password, walletName: credentials.name);
73
+ final wallet = await _service.restoreFromSeed(credentials);
74
+ appStore.wallet = wallet;
75
+ appStore.authenticationStore.allowed();
76
+ }
77
+
78
+ Future<String> getWalletPassword({String walletName}) async {
79
+ final key = generateStoreKeyFor(
80
+ key: SecretStoreKey.moneroWalletPassword, walletName: walletName);
81
+ final encodedPassword = await secureStorage.read(key: key);
82
+
83
+ return decodeWalletPassword(password: encodedPassword);
84
+ }
85
+
86
+ Future<void> saveWalletPassword({String walletName, String password}) async {
87
+ final key = generateStoreKeyFor(
88
+ key: SecretStoreKey.moneroWalletPassword, walletName: walletName);
89
+ final encodedPassword = encodeWalletPassword(password: password);
90
+
91
+ await secureStorage.write(key: key, value: encodedPassword);
92
}
93
}
lib/core/wallet_credentials.dart
+2
-2
@@ -1,6 +1,6 @@
1
abstract class WalletCredentials {
2
- const WalletCredentials({this.name, this.password});
2
+ WalletCredentials({this.name, this.password});
3
4
final String name;
5
- final String password;
5
+ String password;
6
}
\ No newline at end of file
lib/core/wallet_list_service.dart
deleted
-16
@@ -1,16 +0,0 @@
1
-import 'package:cake_wallet/core/wallet_credentials.dart';
2
-
3
-abstract class WalletListService<N extends WalletCredentials,
4
- RFS extends WalletCredentials, RFK extends WalletCredentials> {
5
- Future<void> create(N credentials);
6
-
7
- Future<void> restoreFromSeed(RFS credentials);
8
-
9
- Future<void> restoreFromKeys(RFK credentials);
10
-
11
- Future<void> openWallet(String name, String password);
12
-
13
- Future<bool> isWalletExit(String name);
14
-
15
- Future<void> remove(String wallet);
16
-}
lib/core/wallet_service.dart
new
+17
@@ -0,0 +1,17 @@
1
+import 'package:cake_wallet/core/wallet_base.dart';
2
+import 'package:cake_wallet/core/wallet_credentials.dart';
3
+
4
+abstract class WalletService<N extends WalletCredentials,
5
+ RFS extends WalletCredentials, RFK extends WalletCredentials> {
6
+ Future<WalletBase> create(N credentials);
7
+
8
+ Future<WalletBase> restoreFromSeed(RFS credentials);
9
+
10
+ Future<WalletBase> restoreFromKeys(RFK credentials);
11
+
12
+ Future<WalletBase> openWallet(String name, String password);
13
+
14
+ Future<bool> isWalletExit(String name);
15
+
16
+ Future<void> remove(String wallet);
17
+}
lib/di.dart
new
+105
@@ -0,0 +1,105 @@
1
+import 'package:cake_wallet/core/auth_service.dart';
2
+import 'package:cake_wallet/src/screens/auth/auth_page.dart';
3
+import 'package:cake_wallet/src/screens/dashboard/dashboard_page.dart';
4
+import 'package:cake_wallet/src/screens/receive/receive_page.dart';
5
+import 'package:cake_wallet/src/screens/subaddress/address_edit_or_create_page.dart';
6
+import 'package:cake_wallet/view_model/address_list/address_edit_or_create_view_model.dart';
7
+import 'package:cake_wallet/view_model/auth_view_model.dart';
8
+import 'package:cake_wallet/view_model/dashboard_view_model.dart';
9
+import 'package:cake_wallet/view_model/address_list/address_list_view_model.dart';
10
+import 'package:get_it/get_it.dart';
11
+import 'package:http/http.dart';
12
+import 'package:mobx/mobx.dart';
13
+import 'package:shared_preferences/shared_preferences.dart';
14
+import 'package:flutter_secure_storage/flutter_secure_storage.dart';
15
+import 'package:cake_wallet/view_model/wallet_restoration_from_seed_vm.dart';
16
+import 'package:cake_wallet/core/wallet_base.dart';
17
+import 'package:cake_wallet/core/wallet_creation_service.dart';
18
+import 'package:cake_wallet/store/app_store.dart';
19
+import 'package:cake_wallet/src/domain/common/wallet_type.dart';
20
+import 'package:cake_wallet/view_model/wallet_new_vm.dart';
21
+import 'package:cake_wallet/store/authentication_store.dart';
22
+
23
+final getIt = GetIt.instance;
24
+
25
+ReactionDisposer _onCurrentWalletChangeReaction;
26
+
27
+void setup() {
28
+ getIt.registerSingleton(AuthenticationStore());
29
+ getIt.registerSingleton<AppStore>(
30
+ AppStore(authenticationStore: getIt.get<AuthenticationStore>()));
31
+ getIt.registerSingleton<FlutterSecureStorage>(FlutterSecureStorage());
32
+ getIt.registerSingletonAsync<SharedPreferences>(
33
+ () => SharedPreferences.getInstance());
34
+ getIt.registerFactoryParam<WalletCreationService, WalletType, void>(
35
+ (type, _) => WalletCreationService(
36
+ initialType: type,
37
+ appStore: getIt.get<AppStore>(),
38
+ secureStorage: getIt.get<FlutterSecureStorage>(),
39
+ sharedPreferences: getIt.get<SharedPreferences>()));
40
+
41
+ getIt.registerFactoryParam<WalletNewVM, WalletType, void>((type, _) =>
42
+ WalletNewVM(getIt.get<WalletCreationService>(param1: type), type: type));
43
+
44
+ getIt
45
+ .registerFactoryParam<WalletRestorationFromSeedVM, List, void>((args, _) {
46
+ final type = args.first as WalletType;
47
+ final language = args[1] as String;
48
+ final mnemonic = args[2] as String;
49
+
50
+ return WalletRestorationFromSeedVM(
51
+ getIt.get<WalletCreationService>(param1: type),
52
+ type: type,
53
+ language: language,
54
+ seed: mnemonic);
55
+ });
56
+
57
+ getIt.registerFactory<AddressListViewModel>(
58
+ () => AddressListViewModel(wallet: getIt.get<AppStore>().wallet));
59
+
60
+ getIt.registerFactory(
61
+ () => DashboardViewModel(appStore: getIt.get<AppStore>()));
62
+
63
+ getIt.registerFactory<AuthService>(() => AuthService(
64
+ secureStorage: getIt.get<FlutterSecureStorage>(),
65
+ sharedPreferences: getIt.get<SharedPreferences>()));
66
+
67
+ getIt.registerFactory<AuthViewModel>(() => AuthViewModel(
68
+ authService: getIt.get<AuthService>(),
69
+ sharedPreferences: getIt.get<SharedPreferences>()));
70
+
71
+ getIt.registerFactory<AuthPage>(() => AuthPage(
72
+ authViewModel: getIt.get<AuthViewModel>(),
73
+ onAuthenticationFinished: (isAuthenticated, __) {
74
+ if (isAuthenticated) {
75
+ getIt.get<AuthenticationStore>().allowed();
76
+ }
77
+ },
78
+ closable: false));
79
+
80
+ getIt.registerFactory<DashboardPage>(() => DashboardPage(
81
+ walletViewModel: getIt.get<DashboardViewModel>(),
82
+ ));
83
+
84
+ getIt.registerFactory<ReceivePage>(() =>
85
+ ReceivePage(addressListViewModel: getIt.get<AddressListViewModel>()));
86
+
87
+ getIt.registerFactoryParam<AddressEditOrCreateViewModel, dynamic, void>(
88
+ (dynamic item, _) => AddressEditOrCreateViewModel(
89
+ wallet: getIt.get<AppStore>().wallet, item: item));
90
+
91
+ getIt.registerFactoryParam<AddressEditOrCreatePage, dynamic, void>(
92
+ (dynamic item, _) => AddressEditOrCreatePage(
93
+ addressEditOrCreateViewModel:
94
+ getIt.get<AddressEditOrCreateViewModel>(param1: item)));
95
+
96
+ final appStore = getIt.get<AppStore>();
97
+
98
+ _onCurrentWalletChangeReaction ??=
99
+ reaction((_) => appStore.wallet, (WalletBase wallet) async {
100
+ print('Wallet name ${wallet.name}');
101
+ await getIt
102
+ .get<SharedPreferences>()
103
+ .setString('current_wallet_name', wallet.name);
104
+ });
105
+}
lib/main.dart
+27
-12
@@ -1,7 +1,13 @@
1
-import 'package:cake_wallet/core/app_service.dart';
1
+import 'package:cake_wallet/reactions/bootstrap.dart';
2
+import 'package:cake_wallet/store/authentication_store.dart';
3
import 'package:cake_wallet/core/auth_service.dart';
4
+import 'package:cake_wallet/bitcoin/bitcoin_wallet_service.dart';
5
+import 'package:cake_wallet/monero/monero_wallet_service.dart';
6
import 'package:cake_wallet/core/wallet_creation_service.dart';
7
+import 'package:cake_wallet/di.dart';
8
+import 'package:cake_wallet/view_model/wallet_new_vm.dart';
9
import 'package:flutter_localizations/flutter_localizations.dart';
10
+import 'package:get_it/get_it.dart';
11
import 'package:path_provider/path_provider.dart';
12
import 'package:shared_preferences/shared_preferences.dart';
13
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
@@ -27,7 +33,8 @@ import 'package:cake_wallet/src/stores/wallet/wallet_store.dart';
33
import 'package:cake_wallet/src/stores/send_template/send_template_store.dart';
34
import 'package:cake_wallet/src/stores/exchange_template/exchange_template_store.dart';
35
import 'package:cake_wallet/src/screens/root/root.dart';
30
-import 'package:cake_wallet/src/stores/authentication/authentication_store.dart';
36
+
37
+//import 'package:cake_wallet/src/stores/authentication/authentication_store.dart';
38
import 'package:cake_wallet/src/stores/settings/settings_store.dart';
39
import 'package:cake_wallet/src/stores/price/price_store.dart';
40
import 'package:cake_wallet/src/domain/services/user_service.dart';
@@ -47,6 +54,8 @@ import 'package:cake_wallet/src/stores/seed_language/seed_language_store.dart';
54
void main() async {
55
WidgetsFlutterBinding.ensureInitialized();
56
57
+ setup();
58
+
59
final appDir = await getApplicationDocumentsDirectory();
60
Hive.init(appDir.path);
61
Hive.registerAdapter(ContactAdapter());
@@ -87,13 +96,13 @@ void main() async {
96
sharedPreferences: sharedPreferences);
97
final userService = UserService(
98
sharedPreferences: sharedPreferences, secureStorage: secureStorage);
90
- final authenticationStore = AuthenticationStore(userService: userService);
99
+// final authenticationStore = AuthenticationStore(userService: userService);
100
101
await initialSetup(
102
sharedPreferences: sharedPreferences,
103
walletListService: walletListService,
104
nodes: nodes,
96
- authStore: authenticationStore,
105
+// authStore: authenticationStore,
106
initialMigrationVersion: 2);
107
108
final settingsStore = await SettingsStoreBase.load(
@@ -119,8 +128,7 @@ void main() async {
128
129
final walletCreationService = WalletCreationService();
130
final authService = AuthService();
122
- final appStore = AppService(
123
- walletCreationService: walletCreationService, authService: authService);
131
+
132
133
setReactions(
134
settingsStore: settingsStore,
@@ -128,7 +136,7 @@ void main() async {
136
syncStore: syncStore,
137
walletStore: walletStore,
138
walletService: walletService,
131
- authenticationStore: authenticationStore,
139
+// authenticationStore: authenticationStore,
140
loginStore: loginStore);
141
142
runApp(MultiProvider(providers: [
@@ -141,7 +149,7 @@ void main() async {
149
Provider(create: (_) => walletStore),
150
Provider(create: (_) => syncStore),
151
Provider(create: (_) => balanceStore),
144
- Provider(create: (_) => authenticationStore),
152
+// Provider(create: (_) => authenticationStore),
153
Provider(create: (_) => contacts),
154
Provider(create: (_) => nodes),
155
Provider(create: (_) => transactionDescriptions),
@@ -149,7 +157,7 @@ void main() async {
157
Provider(create: (_) => seedLanguageStore),
158
Provider(create: (_) => sendTemplateStore),
159
Provider(create: (_) => exchangeTemplateStore),
152
- Provider(create: (_) => appStore),
160
+// Provider(create: (_) => appStore),
161
Provider(create: (_) => walletCreationService),
162
Provider(create: (_) => authService)
163
], child: CakeWalletApp()));
@@ -159,7 +167,7 @@ Future<void> initialSetup(
167
{WalletListService walletListService,
168
SharedPreferences sharedPreferences,
169
Box<Node> nodes,
162
- AuthenticationStore authStore,
170
+// AuthenticationStore authStore,
171
int initialMigrationVersion = 1,
172
WalletType initialWalletType = WalletType.bitcoin}) async {
173
await walletListService.changeWalletManger(walletType: initialWalletType);
@@ -167,7 +175,12 @@ Future<void> initialSetup(
175
version: initialMigrationVersion,
176
sharedPreferences: sharedPreferences,
177
nodes: nodes);
170
- await authStore.started();
178
+// await authStore.started();
179
+ await bootstrap();
180
+// final authenticationStore = getIt.get<AuthenticationStore>();
181
+ // FIXME
182
+// authenticationStore.state = AuthenticationState.denied;
183
+
184
monero_wallet.onStartup();
185
}
186
@@ -241,6 +254,8 @@ class MaterialAppWithTheme extends StatelessWidget {
254
nodes: nodes,
255
trades: trades,
256
transactionDescriptions: transactionDescriptions),
244
- home: Root());
257
+ home: Root(
258
+ authenticationStore: getIt.get<AuthenticationStore>(),
259
+ ));
260
}
261
}
lib/monero/monero_balance.dart
renamed
lib/monero/monero_subaddress_list.dart
new
+76
@@ -0,0 +1,76 @@
1
+import 'package:flutter/services.dart';
2
+import 'package:mobx/mobx.dart';
3
+import 'package:cw_monero/subaddress_list.dart' as subaddress_list;
4
+import 'package:cake_wallet/src/domain/monero/subaddress.dart';
5
+
6
+part 'monero_subaddress_list.g.dart';
7
+
8
+class MoneroSubaddressList = MoneroSubaddressListBase
9
+ with _$MoneroSubaddressList;
10
+
11
+abstract class MoneroSubaddressListBase with Store {
12
+ MoneroSubaddressListBase() {
13
+ _isRefreshing = false;
14
+ _isUpdating = false;
15
+ subaddresses = ObservableList<Subaddress>();
16
+ }
17
+
18
+ @observable
19
+ ObservableList<Subaddress> subaddresses;
20
+
21
+ bool _isRefreshing;
22
+ bool _isUpdating;
23
+
24
+ void update({int accountIndex}) {
25
+ if (_isUpdating) {
26
+ return;
27
+ }
28
+
29
+ try {
30
+ _isUpdating = true;
31
+ refresh(accountIndex: accountIndex);
32
+ subaddresses.clear();
33
+ subaddresses.addAll(getAll());
34
+ _isUpdating = false;
35
+ } catch (e) {
36
+ _isUpdating = false;
37
+ rethrow;
38
+ }
39
+ }
40
+
41
+ List<Subaddress> getAll() {
42
+ return subaddress_list
43
+ .getAllSubaddresses()
44
+ .map((subaddressRow) => Subaddress.fromRow(subaddressRow))
45
+ .toList();
46
+ }
47
+
48
+ Future addSubaddress({int accountIndex, String label}) async {
49
+ await subaddress_list.addSubaddress(
50
+ accountIndex: accountIndex, label: label);
51
+ update(accountIndex: accountIndex);
52
+ }
53
+
54
+ Future setLabelSubaddress(
55
+ {int accountIndex, int addressIndex, String label}) async {
56
+ await subaddress_list.setLabelForSubaddress(
57
+ accountIndex: accountIndex, addressIndex: addressIndex, label: label);
58
+ update(accountIndex: accountIndex);
59
+ }
60
+
61
+ void refresh({int accountIndex}) {
62
+ if (_isRefreshing) {
63
+ return;
64
+ }
65
+
66
+ try {
67
+ _isRefreshing = true;
68
+ subaddress_list.refreshSubaddresses(accountIndex: accountIndex);
69
+ _isRefreshing = false;
70
+ } on PlatformException catch (e) {
71
+ _isRefreshing = false;
72
+ print(e);
73
+ rethrow;
74
+ }
75
+ }
76
+}
lib/monero/monero_transaction_history.dart
renamed
+7
-3
@@ -8,7 +8,7 @@ import 'package:cake_wallet/src/domain/monero/monero_transaction_info.dart';
8
9
part 'monero_transaction_history.g.dart';
10
11
-List<TransactionInfo> _getAllTransactions(dynamic _) =>
11
+List<MoneroTransactionInfo> _getAllTransactions(dynamic _) =>
12
monero_transaction_history
13
.getAllTransations()
14
.map((row) => MoneroTransactionInfo.fromRow(row))
@@ -18,9 +18,13 @@ class MoneroTransactionHistory = MoneroTransactionHistoryBase
18
with _$MoneroTransactionHistory;
19
20
abstract class MoneroTransactionHistoryBase
21
- extends TranasctionHistoryBase<TransactionInfo> with Store {
21
+ extends TransactionHistoryBase<MoneroTransactionInfo> with Store {
22
+ MoneroTransactionHistoryBase() {
23
+ transactions = ObservableList<MoneroTransactionInfo>();
24
+ }
25
+
26
@override
23
- Future<List<TransactionInfo>> fetchTransactions() async {
27
+ Future<List<MoneroTransactionInfo>> fetchTransactions() async {
28
monero_transaction_history.refreshTransactions();
29
return _getAllTransactions(null);
30
}
lib/monero/monero_wallet.dart
renamed
+32
-21
@@ -1,35 +1,36 @@
1
-import 'package:cake_wallet/core/monero_balance.dart';
2
-import 'package:cake_wallet/core/monero_transaction_history.dart';
1
+import 'package:cake_wallet/src/domain/common/wallet_type.dart';
2
+import 'package:flutter/foundation.dart';
3
+import 'package:mobx/mobx.dart';
4
+import 'package:cake_wallet/monero/monero_balance.dart';
5
+import 'package:cake_wallet/monero/monero_transaction_history.dart';
6
+import 'package:cake_wallet/monero/monero_subaddress_list.dart';
7
+import 'package:cake_wallet/core/wallet_base.dart';
8
+import 'package:cake_wallet/core/transaction_history.dart';
9
import 'package:cake_wallet/src/domain/common/sync_status.dart';
10
import 'package:cake_wallet/src/domain/monero/account.dart';
11
import 'package:cake_wallet/src/domain/monero/account_list.dart';
12
import 'package:cake_wallet/src/domain/monero/subaddress.dart';
7
-import 'package:cake_wallet/src/domain/monero/subaddress_list.dart';
13
import 'package:cw_monero/wallet.dart';
9
-import 'package:flutter/foundation.dart';
10
-import 'package:mobx/mobx.dart';
14
import 'package:cake_wallet/src/domain/common/node.dart';
15
import 'package:cw_monero/wallet.dart' as monero_wallet;
13
-import 'wallet_base.dart';
16
17
part 'monero_wallet.g.dart';
18
19
class MoneroWallet = MoneroWalletBase with _$MoneroWallet;
20
21
abstract class MoneroWalletBase extends WalletBase<MoneroBalance> with Store {
20
- MoneroWalletBase({String filename, this.isRecovery = false}) {
21
- transactionHistory = MoneroTransactionHistory();
22
+ MoneroWalletBase({String filename, this.isRecovery = false})
23
+ : transactionHistory = MoneroTransactionHistory() {
24
_filename = filename;
25
accountList = AccountList();
24
- subaddressList = SubaddressList();
26
+ subaddressList = MoneroSubaddressList();
27
balance = MoneroBalance(
28
fullBalance: monero_wallet.getFullBalance(accountIndex: 0),
29
unlockedBalance: monero_wallet.getFullBalance(accountIndex: 0));
30
}
31
30
- MoneroTransactionHistory transactionHistory;
31
- SubaddressList subaddressList;
32
- AccountList accountList;
32
+ @override
33
+ final MoneroTransactionHistory transactionHistory;
34
35
@observable
36
Account account;
@@ -41,30 +42,40 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance> with Store {
42
SyncStatus syncStatus;
43
44
@override
44
- String get name => filename.split('/').last;
45
+ String get name => _filename.split('/').last;
46
47
@override
47
- String get filename => _filename;
48
+ final type = WalletType.monero;
49
49
- String _filename;
50
+ @override
51
+ @observable
52
+ String address;
53
54
bool isRecovery;
55
53
- SyncListner _listner;
56
+ MoneroSubaddressList subaddressList;
57
+
58
+ AccountList accountList;
59
+
60
+ String _filename;
61
+
62
+ SyncListner _listener;
63
55
- void init() {
64
+ Future<void> init() async {
65
+ await accountList.update();
66
account = accountList.getAll().first;
57
- subaddressList.refresh(accountIndex: account.id ?? 0);
67
+ subaddressList.update(accountIndex: account.id ?? 0);
68
subaddress = subaddressList.getAll().first;
69
balance = MoneroBalance(
70
fullBalance: monero_wallet.getFullBalance(accountIndex: account.id),
71
unlockedBalance:
72
monero_wallet.getFullBalance(accountIndex: account.id));
73
+ address = subaddress.address;
74
_setListeners();
75
}
76
77
void close() {
67
- _listner?.stop();
78
+ _listener?.stop();
79
}
80
81
@override
@@ -133,8 +144,8 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance> with Store {
144
Future<bool> isConnected() async => monero_wallet.isConnected();
145
146
void _setListeners() {
136
- _listner?.stop();
137
- _listner = monero_wallet.setListeners(
147
+ _listener?.stop();
148
+ _listener = monero_wallet.setListeners(
149
_onNewBlock, _onNeedToRefresh, _onNewTransaction);
150
}
151
lib/monero/monero_wallet_service.dart
renamed
+26
-15
@@ -1,21 +1,20 @@
1
-import 'package:cake_wallet/core/monero_wallet.dart';
1
+import 'package:cake_wallet/monero/monero_wallet.dart';
2
import 'package:cake_wallet/core/wallet_credentials.dart';
3
-import 'package:cake_wallet/core/wallet_list_service.dart';
3
+import 'package:cake_wallet/core/wallet_service.dart';
4
import 'package:cake_wallet/src/domain/common/pathForWallet.dart';
5
import 'package:cake_wallet/src/domain/common/wallet_type.dart';
6
import 'package:cw_monero/wallet_manager.dart' as monero_wallet_manager;
7
import 'package:cw_monero/wallet.dart' as monero_wallet;
8
9
class MoneroNewWalletCredentials extends WalletCredentials {
10
- const MoneroNewWalletCredentials(
11
- {String name, String password, this.language})
10
+ MoneroNewWalletCredentials({String name, String password, this.language})
11
: super(name: name, password: password);
12
13
final String language;
14
}
15
16
class MoneroRestoreWalletFromSeedCredentials extends WalletCredentials {
18
- const MoneroRestoreWalletFromSeedCredentials(
17
+ MoneroRestoreWalletFromSeedCredentials(
18
{String name, String password, this.mnemonic, this.height})
19
: super(name: name, password: password);
20
@@ -24,7 +23,7 @@ class MoneroRestoreWalletFromSeedCredentials extends WalletCredentials {
23
}
24
25
class MoneroRestoreWalletFromKeysCredentials extends WalletCredentials {
27
- const MoneroRestoreWalletFromKeysCredentials(
26
+ MoneroRestoreWalletFromKeysCredentials(
27
{String name,
28
String password,
29
this.language,
@@ -41,12 +40,12 @@ class MoneroRestoreWalletFromKeysCredentials extends WalletCredentials {
40
final int height;
41
}
42
44
-class MoneroWalletListService extends WalletListService<
43
+class MoneroWalletService extends WalletService<
44
MoneroNewWalletCredentials,
45
MoneroRestoreWalletFromSeedCredentials,
46
MoneroRestoreWalletFromKeysCredentials> {
47
@override
49
- Future<void> create(MoneroNewWalletCredentials credentials) async {
48
+ Future<MoneroWallet> create(MoneroNewWalletCredentials credentials) async {
49
try {
50
final path =
51
await pathForWallet(name: credentials.name, type: WalletType.monero);
@@ -56,7 +55,10 @@ class MoneroWalletListService extends WalletListService<
55
password: credentials.password,
56
language: credentials.language);
57
59
- return MoneroWallet(filename: monero_wallet.getFilename())..init();
58
+ final wallet = MoneroWallet(filename: monero_wallet.getFilename());
59
+ await wallet.init();
60
+
61
+ return wallet;
62
} catch (e) {
63
// TODO: Implement Exception fop wallet list service.
64
print('MoneroWalletsManager Error: $e');
@@ -77,7 +79,7 @@ class MoneroWalletListService extends WalletListService<
79
}
80
81
@override
80
- Future<void> openWallet(String name, String password) async {
82
+ Future<MoneroWallet> openWallet(String name, String password) async {
83
try {
84
final path = await pathForWallet(name: name, type: WalletType.monero);
85
monero_wallet_manager.openWallet(path: path, password: password);
@@ -86,7 +88,10 @@ class MoneroWalletListService extends WalletListService<
88
// final walletInfo = walletInfoSource.values
89
// .firstWhere((info) => info.id == id, orElse: () => null);
90
89
- return MoneroWallet(filename: monero_wallet.getFilename())..init();
91
+ final wallet = MoneroWallet(filename: monero_wallet.getFilename());
92
+ await wallet.init();
93
+
94
+ return wallet;
95
} catch (e) {
96
// TODO: Implement Exception fop wallet list service.
97
print('MoneroWalletsManager Error: $e');
@@ -100,7 +105,7 @@ class MoneroWalletListService extends WalletListService<
105
}
106
107
@override
103
- Future<void> restoreFromKeys(
108
+ Future<MoneroWallet> restoreFromKeys(
109
MoneroRestoreWalletFromKeysCredentials credentials) async {
110
try {
111
final path =
@@ -115,7 +120,10 @@ class MoneroWalletListService extends WalletListService<
120
viewKey: credentials.viewKey,
121
spendKey: credentials.spendKey);
122
118
- return MoneroWallet(filename: monero_wallet.getFilename())..init();
123
+ final wallet = MoneroWallet(filename: monero_wallet.getFilename());
124
+ await wallet.init();
125
+
126
+ return wallet;
127
} catch (e) {
128
// TODO: Implement Exception fop wallet list service.
129
print('MoneroWalletsManager Error: $e');
@@ -124,7 +132,7 @@ class MoneroWalletListService extends WalletListService<
132
}
133
134
@override
127
- Future<void> restoreFromSeed(
135
+ Future<MoneroWallet> restoreFromSeed(
136
MoneroRestoreWalletFromSeedCredentials credentials) async {
137
try {
138
final path =
@@ -136,7 +144,10 @@ class MoneroWalletListService extends WalletListService<
144
seed: credentials.mnemonic,
145
restoreHeight: credentials.height);
146
139
- return MoneroWallet(filename: monero_wallet.getFilename())..init();
147
+ final wallet = MoneroWallet(filename: monero_wallet.getFilename());
148
+ await wallet.init();
149
+
150
+ return wallet;
151
} catch (e) {
152
// TODO: Implement Exception fop wallet list service.
153
print('MoneroWalletsManager Error: $e');
lib/palette.dart
+1
@@ -12,6 +12,7 @@ class Palette {
12
static const Color blue = Color.fromRGBO(88, 143, 252, 1.0);
13
static const Color darkLavender = Color.fromRGBO(225, 238, 250, 1.0);
14
static const Color nightBlue = Color.fromRGBO(46, 57, 96, 1.0);
15
+ static const Color eee = Color.fromRGBO(236, 239, 245, 1.0);
16
}
17
18
class PaletteDark {
lib/reactions/bootstrap.dart
new
+66
@@ -0,0 +1,66 @@
1
+import 'package:mobx/mobx.dart';
2
+import 'package:cake_wallet/di.dart';
3
+import 'package:shared_preferences/shared_preferences.dart';
4
+import 'package:flutter_secure_storage/flutter_secure_storage.dart';
5
+import 'package:cake_wallet/bitcoin/bitcoin_wallet_service.dart';
6
+import 'package:cake_wallet/monero/monero_wallet_service.dart';
7
+import 'package:cake_wallet/core/wallet_service.dart';
8
+import 'package:cake_wallet/store/app_store.dart';
9
+import 'package:cake_wallet/store/authentication_store.dart';
10
+import 'package:cake_wallet/src/domain/common/wallet_type.dart';
11
+import 'package:cake_wallet/src/domain/common/secret_store_key.dart';
12
+import 'package:cake_wallet/src/domain/common/encrypt.dart';
13
+
14
+// FIXME: move me
15
+Future<String> getWalletPassword({String walletName}) async {
16
+ final secureStorage = getIt.get<FlutterSecureStorage>();
17
+ final key = generateStoreKeyFor(
18
+ key: SecretStoreKey.moneroWalletPassword, walletName: walletName);
19
+ final encodedPassword = await secureStorage.read(key: key);
20
+
21
+ return decodeWalletPassword(password: encodedPassword);
22
+}
23
+
24
+// FIXME: move me
25
+Future<void> loadCurrentWallet() async {
26
+ final appStore = getIt.get<AppStore>();
27
+ final name = getIt.get<SharedPreferences>().getString('current_wallet_name');
28
+ final type = WalletType.monero; // FIXME
29
+ final password = await getWalletPassword(walletName: name);
30
+
31
+ WalletService _service;
32
+ switch (type) {
33
+ case WalletType.monero:
34
+ _service = MoneroWalletService();
35
+ break;
36
+ case WalletType.bitcoin:
37
+ _service = BitcoinWalletService();
38
+ break;
39
+ default:
40
+ break;
41
+ }
42
+
43
+ final wallet = await _service.openWallet(name, password);
44
+ appStore.wallet = wallet;
45
+}
46
+
47
+ReactionDisposer _initialAuthReaction;
48
+
49
+Future<void> bootstrap() async {
50
+ final authenticationStore = getIt.get<AuthenticationStore>();
51
+
52
+ if (authenticationStore.state == AuthenticationState.uninitialized) {
53
+ authenticationStore.state =
54
+ getIt.get<SharedPreferences>().getString('current_wallet_name') == null
55
+ ? AuthenticationState.denied
56
+ : AuthenticationState.installed;
57
+ }
58
+
59
+ _initialAuthReaction ??= autorun((_) async {
60
+ final state = authenticationStore.state;
61
+
62
+ if (state == AuthenticationState.installed) {
63
+ await loadCurrentWallet();
64
+ }
65
+ });
66
+}
lib/router.dart
+108
-76
@@ -1,3 +1,5 @@
1
+import 'package:cake_wallet/view_model/wallet_new_vm.dart';
2
+import 'package:cake_wallet/view_model/wallet_restoration_from_seed_vm.dart';
3
import 'package:flutter/cupertino.dart';
4
import 'package:flutter/material.dart';
5
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
@@ -6,7 +8,7 @@ import 'package:provider/provider.dart';
8
import 'package:hive/hive.dart';
9
import 'package:cake_wallet/routes.dart';
10
import 'package:cake_wallet/generated/i18n.dart';
9
-
11
+import 'di.dart';
12
// MARK: Import domains
13
14
import 'package:cake_wallet/src/domain/common/contact.dart';
@@ -21,7 +23,7 @@ import 'package:cake_wallet/src/domain/common/node.dart';
23
import 'package:cake_wallet/src/domain/monero/transaction_description.dart';
24
import 'package:cake_wallet/src/domain/exchange/trade.dart';
25
import 'package:cake_wallet/src/domain/monero/account.dart';
24
-import 'package:cake_wallet/src/domain/common/mnemotic_item.dart';
26
+import 'package:cake_wallet/src/domain/common/mnemonic_item.dart';
27
import 'package:cake_wallet/src/domain/common/transaction_info.dart';
28
import 'package:cake_wallet/src/domain/monero/subaddress.dart';
29
import 'package:cake_wallet/src/domain/common/wallet_type.dart';
@@ -56,7 +58,7 @@ import 'package:cake_wallet/src/screens/auth/auth_page.dart';
58
import 'package:cake_wallet/src/screens/nodes/new_node_page.dart';
59
import 'package:cake_wallet/src/screens/nodes/nodes_list_page.dart';
60
import 'package:cake_wallet/src/screens/receive/receive_page.dart';
59
-import 'package:cake_wallet/src/screens/subaddress/new_subaddress_page.dart';
61
+import 'package:cake_wallet/src/screens/subaddress/address_edit_or_create_page.dart';
62
import 'package:cake_wallet/src/screens/wallet_list/wallet_list_page.dart';
63
import 'package:cake_wallet/src/screens/new_wallet/new_wallet_page.dart';
64
import 'package:cake_wallet/src/screens/setup_pin_code/setup_pin_code.dart';
@@ -126,24 +128,18 @@ class Router {
128
Navigator.pushNamed(context, Routes.newWalletType))));
129
130
case Routes.newWalletType:
129
- return CupertinoPageRoute<void>(builder: (_) => NewWalletTypePage());
131
+ return CupertinoPageRoute<void>(
132
+ builder: (_) => NewWalletTypePage(
133
+ onTypeSelected: (context, type) => Navigator.of(context)
134
+ .pushNamed(Routes.newWallet, arguments: type),
135
+ ));
136
137
case Routes.newWallet:
138
final type = settings.arguments as WalletType;
133
- walletListService.changeWalletManger(walletType: type);
139
+ final walletNewVM = getIt.get<WalletNewVM>(param1: type);
140
141
return CupertinoPageRoute<void>(
136
- builder:
137
- (_) =>
138
- ProxyProvider<AuthenticationStore, WalletCreationStore>(
139
- update: (_, authStore, __) => WalletCreationStore(
140
- authStore: authStore,
141
- sharedPreferences: sharedPreferences,
142
- walletListService: walletListService),
143
- child: NewWalletPage(
144
- walletsService: walletListService,
145
- walletService: walletService,
146
- sharedPreferences: sharedPreferences)));
142
+ builder: (_) => NewWalletPage(walletNewVM));
143
144
case Routes.setupPin:
145
Function(BuildContext, String) callback;
@@ -163,6 +159,13 @@ class Router {
159
callback == null ? null : callback(context, pin))),
160
fullscreenDialog: true);
161
162
+ case Routes.restoreWalletType:
163
+ return CupertinoPageRoute<void>(
164
+ builder: (_) => NewWalletTypePage(
165
+ onTypeSelected: (context, type) => Navigator.of(context)
166
+ .pushNamed(Routes.restoreWalletOptions, arguments: type),
167
+ ));
168
+
169
case Routes.restoreOptions:
170
final type = settings.arguments as WalletType;
171
walletListService.changeWalletManger(walletType: type);
@@ -175,7 +178,28 @@ class Router {
178
walletListService.changeWalletManger(walletType: type);
179
180
return CupertinoPageRoute<void>(
178
- builder: (_) => RestoreWalletOptionsPage(type: type));
181
+ builder: (_) => RestoreWalletOptionsPage(
182
+ type: type,
183
+ onRestoreFromSeed: (context) {
184
+ final route = type == WalletType.monero
185
+ ? Routes.seedLanguage
186
+ : Routes.restoreWalletFromSeed;
187
+ final args = type == WalletType.monero
188
+ ? [type, Routes.restoreWalletFromSeed]
189
+ : [type];
190
+
191
+ Navigator.of(context).pushNamed(route, arguments: args);
192
+ },
193
+ onRestoreFromKeys: (context) {
194
+ final route = type == WalletType.monero
195
+ ? Routes.seedLanguage
196
+ : Routes.restoreWalletFromKeys;
197
+ final args = type == WalletType.monero
198
+ ? [type, Routes.restoreWalletFromSeed]
199
+ : [type];
200
+
201
+ Navigator.of(context).pushNamed(route, arguments: args);
202
+ }));
203
204
case Routes.restoreWalletOptionsFromWelcome:
205
return CupertinoPageRoute<void>(
@@ -186,7 +210,7 @@ class Router {
210
sharedPreferences: sharedPreferences)),
211
child: SetupPinCodePage(
212
onPinCodeSetup: (context, _) => Navigator.pushNamed(
189
- context, Routes.restoreWalletOptions))));
213
+ context, Routes.restoreWalletType))));
214
215
case Routes.seed:
216
return MaterialPageRoute<void>(
@@ -196,8 +220,11 @@ class Router {
220
callback: settings.arguments as void Function()));
221
222
case Routes.restoreWalletFromSeed:
199
- final type = settings.arguments as WalletType;
200
- walletListService.changeWalletManger(walletType: type);
223
+ final args = settings.arguments as List<dynamic>;
224
+ final type = args.first as WalletType;
225
+ final language = type == WalletType.monero
226
+ ? args[1] as String
227
+ : 'English'; // FIXME: Unnamed constant; English default and only one language for bitcoin.
228
229
return CupertinoPageRoute<void>(
230
builder: (_) =>
@@ -207,11 +234,15 @@ class Router {
234
sharedPreferences: sharedPreferences,
235
walletListService: walletListService),
236
child: RestoreWalletFromSeedPage(
210
- walletsService: walletListService,
211
- walletService: walletService,
212
- sharedPreferences: sharedPreferences)));
237
+ type: type, language: language)));
238
239
case Routes.restoreWalletFromKeys:
240
+ final args = settings.arguments as List<dynamic>;
241
+ final type = args.first as WalletType;
242
+ final language = type == WalletType.monero
243
+ ? args[1] as String
244
+ : 'English'; // FIXME: Unnamed constant; English default and only one language for bitcoin.
245
+
246
return CupertinoPageRoute<void>(
247
builder: (_) =>
248
ProxyProvider<AuthenticationStore, WalletRestorationStore>(
@@ -256,22 +287,16 @@ class Router {
287
288
case Routes.sendTemplate:
289
return CupertinoPageRoute<void>(
259
- builder: (_) => Provider(
260
- create: (_) => SendStore(
261
- walletService: walletService,
262
- priceStore: priceStore,
263
- transactionDescriptions: transactionDescriptions),
264
- child: SendTemplatePage())
265
- );
290
+ builder: (_) => Provider(
291
+ create: (_) => SendStore(
292
+ walletService: walletService,
293
+ priceStore: priceStore,
294
+ transactionDescriptions: transactionDescriptions),
295
+ child: SendTemplatePage()));
296
297
case Routes.receive:
298
return CupertinoPageRoute<void>(
269
- fullscreenDialog: true,
270
- builder: (_) => MultiProvider(providers: [
271
- Provider(
272
- create: (_) =>
273
- SubaddressListStore(walletService: walletService))
274
- ], child: ReceivePage()));
299
+ fullscreenDialog: true, builder: (_) => getIt.get<ReceivePage>());
300
301
case Routes.transactionDetails:
302
return CupertinoPageRoute<void>(
@@ -281,10 +306,8 @@ class Router {
306
307
case Routes.newSubaddress:
308
return CupertinoPageRoute<void>(
284
- builder: (_) => Provider(
285
- create: (_) =>
286
- SubadrressCreationStore(walletService: walletService),
287
- child: NewSubaddressPage()));
309
+ builder: (_) =>
310
+ getIt.get<AddressEditOrCreatePage>(param1: settings.arguments));
311
312
case Routes.disclaimer:
313
return CupertinoPageRoute<void>(builder: (_) => DisclaimerPage());
@@ -294,7 +317,15 @@ class Router {
317
builder: (_) => DisclaimerPage(isReadOnly: true));
318
319
case Routes.seedLanguage:
297
- return CupertinoPageRoute<void>(builder: (_) => SeedLanguage());
320
+ final args = settings.arguments as List<dynamic>;
321
+ final type = args.first as WalletType;
322
+ final redirectRoute = args[1] as String;
323
+
324
+ return CupertinoPageRoute<void>(builder: (_) {
325
+ return SeedLanguage(
326
+ onConfirm: (context, lang) => Navigator.of(context)
327
+ .popAndPushNamed(redirectRoute, arguments: [type, lang]));
328
+ });
329
330
case Routes.walletList:
331
return MaterialPageRoute<void>(
@@ -306,17 +337,18 @@ class Router {
337
child: WalletListPage()));
338
339
case Routes.auth:
309
- return MaterialPageRoute<void>(
310
- fullscreenDialog: true,
311
- builder: (_) => Provider(
312
- create: (_) => AuthStore(
313
- sharedPreferences: sharedPreferences,
314
- userService: userService,
315
- walletService: walletService),
316
- child: AuthPage(
317
- onAuthenticationFinished:
318
- settings.arguments as OnAuthenticationFinished),
319
- ));
340
+ return null;
341
+// return MaterialPageRoute<void>(
342
+// fullscreenDialog: true,
343
+// builder: (_) => Provider(
344
+// create: (_) => AuthStore(
345
+// sharedPreferences: sharedPreferences,
346
+// userService: userService,
347
+// walletService: walletService),
348
+// child: AuthPage(
349
+// onAuthenticationFinished:
350
+// settings.arguments as OnAuthenticationFinished),
351
+// ));
352
353
case Routes.unlock:
354
return MaterialPageRoute<void>(
@@ -455,15 +487,13 @@ class Router {
487
], child: SubaddressListPage()));
488
489
case Routes.restoreWalletFromSeedDetails:
490
+ final args = settings.arguments as List;
491
+ final walletRestorationFromSeedVM =
492
+ getIt.get<WalletRestorationFromSeedVM>(param1: args);
493
+
494
return CupertinoPageRoute<void>(
459
- builder: (_) =>
460
- ProxyProvider<AuthenticationStore, WalletRestorationStore>(
461
- update: (_, authStore, __) => WalletRestorationStore(
462
- authStore: authStore,
463
- sharedPreferences: sharedPreferences,
464
- walletListService: walletListService,
465
- seed: settings.arguments as List<MnemoticItem>),
466
- child: RestoreWalletFromSeedDetailsPage()));
495
+ builder: (_) => RestoreWalletFromSeedDetailsPage(
496
+ walletRestorationFromSeedVM: walletRestorationFromSeedVM));
497
498
case Routes.exchange:
499
return MaterialPageRoute<void>(
@@ -487,22 +517,24 @@ class Router {
517
518
case Routes.exchangeTemplate:
519
return MaterialPageRoute<void>(
490
- builder: (_) => Provider(create: (_) {
491
- final xmrtoprovider = XMRTOExchangeProvider();
492
-
493
- return ExchangeStore(
494
- initialProvider: xmrtoprovider,
495
- initialDepositCurrency: CryptoCurrency.xmr,
496
- initialReceiveCurrency: CryptoCurrency.btc,
497
- trades: trades,
498
- providerList: [
499
- xmrtoprovider,
500
- ChangeNowExchangeProvider(),
501
- MorphTokenExchangeProvider(trades: trades)
502
- ],
503
- walletStore: walletStore);
504
- }, child: ExchangeTemplatePage(),)
505
- );
520
+ builder: (_) => Provider(
521
+ create: (_) {
522
+ final xmrtoprovider = XMRTOExchangeProvider();
523
+
524
+ return ExchangeStore(
525
+ initialProvider: xmrtoprovider,
526
+ initialDepositCurrency: CryptoCurrency.xmr,
527
+ initialReceiveCurrency: CryptoCurrency.btc,
528
+ trades: trades,
529
+ providerList: [
530
+ xmrtoprovider,
531
+ ChangeNowExchangeProvider(),
532
+ MorphTokenExchangeProvider(trades: trades)
533
+ ],
534
+ walletStore: walletStore);
535
+ },
536
+ child: ExchangeTemplatePage(),
537
+ ));
538
539
case Routes.settings:
540
return MaterialPageRoute<void>(
lib/routes.dart
+1
@@ -46,4 +46,5 @@ class Routes {
46
static const newWalletType = '/new_wallet_type';
47
static const sendTemplate = '/send_template';
48
static const exchangeTemplate = '/exchange_template';
49
+ static const restoreWalletType = '/restore_wallet_type';
50
}
\ No newline at end of file
lib/src/domain/common/balance.dart
+3
-1
@@ -1 +1,3 @@
1
-abstract class Balance {}
\ No newline at end of file
1
+abstract class Balance {
2
+ const Balance();
3
+}
lib/src/domain/common/mnemonic_item.dart
new
+11
@@ -0,0 +1,11 @@
1
+class MnemonicItem {
2
+ MnemonicItem({String text}) : _text = text;
3
+
4
+ String get text => _text;
5
+ String _text;
6
+
7
+ void changeText(String text) => _text = text;
8
+
9
+ @override
10
+ String toString() => text;
11
+}
lib/src/domain/common/mnemotic_item.dart
deleted
-17
@@ -1,17 +0,0 @@
1
-class MnemoticItem {
2
- MnemoticItem({String text, this.dic}) : _text = text;
3
-
4
- String get text => _text;
5
- final List<String> dic;
6
-
7
- String _text;
8
-
9
- bool isCorrect() => dic.contains(text);
10
-
11
- void changeText(String text) {
12
- _text = text;
13
- }
14
-
15
- @override
16
- String toString() => text;
17
-}
lib/src/domain/services/wallet_list_service.dart
+1
-2
@@ -1,5 +1,4 @@
1
import 'dart:async';
2
-import 'package:cake_wallet/bitcoin/bitcoin_wallet.manager.dart';
2
import 'package:cake_wallet/bitcoin/key.dart';
3
import 'package:cake_wallet/src/domain/common/wallet_info.dart';
4
import 'package:flutter/foundation.dart';
@@ -119,7 +118,7 @@ class WalletListService {
118
MoneroWalletsManager(walletInfoSource: walletInfoSource);
119
break;
120
case WalletType.bitcoin:
122
- walletsManager = BitcoinWalletManager();
121
+// walletsManager = BitcoinWalletManager();
122
break;
123
case WalletType.none:
124
walletsManager = null;
lib/src/reactions/set_reactions.dart
+4
-4
@@ -35,10 +35,10 @@ void setReactions(
35
settingsStore: settingsStore,
36
priceStore: priceStore);
37
autorun((_) async {
38
- if (authenticationStore.state == AuthenticationState.allowed) {
39
- await loginStore.loadCurrentWallet();
40
- authenticationStore.state = AuthenticationState.readyToLogin;
41
- }
38
+// if (authenticationStore.state == AuthenticationState.allowed) {
39
+// await loginStore.loadCurrentWallet();
40
+// authenticationStore.state = AuthenticationState.readyToLogin;
41
+// }
42
});
43
}
44
lib/src/screens/auth/auth_page.dart
+65
-52
@@ -1,10 +1,9 @@
1
import 'package:mobx/mobx.dart';
2
-import 'package:provider/provider.dart';
2
import 'package:flutter/material.dart';
3
import 'package:flutter/cupertino.dart';
4
import 'package:cake_wallet/generated/i18n.dart';
6
-import 'package:cake_wallet/src/stores/auth/auth_state.dart';
7
-import 'package:cake_wallet/src/stores/auth/auth_store.dart';
5
+import 'package:cake_wallet/view_model/auth_state.dart';
6
+import 'package:cake_wallet/view_model/auth_view_model.dart';
7
import 'package:cake_wallet/src/screens/pin_code/pin_code.dart';
8
import 'package:cake_wallet/src/stores/settings/settings_store.dart';
9
import 'package:cake_wallet/src/domain/common/biometric_auth.dart';
@@ -12,8 +11,12 @@ import 'package:cake_wallet/src/domain/common/biometric_auth.dart';
11
typedef OnAuthenticationFinished = void Function(bool, AuthPageState);
12
13
class AuthPage extends StatefulWidget {
15
- AuthPage({this.onAuthenticationFinished, this.closable = true});
14
+ AuthPage(
15
+ {this.onAuthenticationFinished,
16
+ this.authViewModel,
17
+ this.closable = true});
18
19
+ final AuthViewModel authViewModel;
20
final OnAuthenticationFinished onAuthenticationFinished;
21
final bool closable;
22
@@ -25,40 +28,13 @@ class AuthPageState extends State<AuthPage> {
28
final _key = GlobalKey<ScaffoldState>();
29
final _pinCodeKey = GlobalKey<PinCodeState>();
30
final _backArrowImageDarkTheme =
28
- Image.asset('assets/images/back_arrow_dark_theme.png');
29
-
30
- void changeProcessText(String text) {
31
- _key.currentState.showSnackBar(
32
- SnackBar(content: Text(text), backgroundColor: Colors.green));
33
- }
34
-
35
- void close() => Navigator.of(_key.currentContext).pop();
31
+ Image.asset('assets/images/back_arrow_dark_theme.png');
32
+ ReactionDisposer _reaction;
33
34
@override
38
- Widget build(BuildContext context) {
39
- final authStore = Provider.of<AuthStore>(context);
40
- final settingsStore = Provider.of<SettingsStore>(context);
41
-
42
- if (settingsStore.allowBiometricalAuthentication) {
43
- WidgetsBinding.instance.addPostFrameCallback((_) {
44
- final biometricAuth = BiometricAuth();
45
- biometricAuth.isAuthenticated().then(
46
- (isAuth) {
47
- if (isAuth) {
48
- authStore.biometricAuth();
49
- _key.currentState.showSnackBar(
50
- SnackBar(
51
- content: Text(S.of(context).authenticated),
52
- backgroundColor: Colors.green,
53
- ),
54
- );
55
- }
56
- }
57
- );
58
- });
59
- }
60
-
61
- reaction((_) => authStore.state, (AuthState state) {
35
+ void initState() {
36
+ _reaction ??=
37
+ reaction((_) => widget.authViewModel.state, (AuthState state) {
38
if (state is AuthenticatedSuccessfully) {
39
WidgetsBinding.instance.addPostFrameCallback((_) {
40
if (widget.onAuthenticationFinished != null) {
@@ -119,32 +95,69 @@ class AuthPageState extends State<AuthPage> {
95
});
96
}
97
});
98
+ super.initState();
99
+ }
100
+
101
+ @override
102
+ void dispose() {
103
+ _reaction.reaction.dispose();
104
+ super.dispose();
105
+ }
106
+
107
+ void changeProcessText(String text) => _key.currentState.showSnackBar(
108
+ SnackBar(content: Text(text), backgroundColor: Colors.green));
109
+
110
+ void close() => Navigator.of(_key.currentContext).pop();
111
+
112
+ @override
113
+ Widget build(BuildContext context) {
114
+// final authStore = Provider.of<AuthStore>(context);
115
+// final settingsStore = Provider.of<SettingsStore>(context);
116
+
117
+// if (settingsStore.allowBiometricalAuthentication) {
118
+// WidgetsBinding.instance.addPostFrameCallback((_) {
119
+// final biometricAuth = BiometricAuth();
120
+// biometricAuth.isAuthenticated().then(
121
+// (isAuth) {
122
+// if (isAuth) {
123
+// authStore.biometricAuth();
124
+// _key.currentState.showSnackBar(
125
+// SnackBar(
126
+// content: Text(S.of(context).authenticated),
127
+// backgroundColor: Colors.green,
128
+// ),
129
+// );
130
+// }
131
+// }
132
+// );
133
+// });
134
+// }
135
136
return Scaffold(
137
key: _key,
138
appBar: CupertinoNavigationBar(
139
leading: widget.closable
127
- ? SizedBox(
128
- height: 37,
129
- width: 20,
130
- child: ButtonTheme(
131
- minWidth: double.minPositive,
132
- child: FlatButton(
133
- highlightColor: Colors.transparent,
134
- splashColor: Colors.transparent,
135
- padding: EdgeInsets.all(0),
136
- onPressed: () => Navigator.of(context).pop(),
137
- child: _backArrowImageDarkTheme),
138
- ),
139
- )
140
- : Container(),
140
+ ? SizedBox(
141
+ height: 37,
142
+ width: 20,
143
+ child: ButtonTheme(
144
+ minWidth: double.minPositive,
145
+ child: FlatButton(
146
+ highlightColor: Colors.transparent,
147
+ splashColor: Colors.transparent,
148
+ padding: EdgeInsets.all(0),
149
+ onPressed: () => Navigator.of(context).pop(),
150
+ child: _backArrowImageDarkTheme),
151
+ ),
152
+ )
153
+ : Container(),
154
backgroundColor: Theme.of(context).backgroundColor,
155
border: null,
156
),
157
resizeToAvoidBottomPadding: false,
158
body: PinCode(
146
- (pin, _) => authStore.auth(
147
- password: pin.fold('', (ac, val) => ac + '$val')),
159
+ (pin, _) => widget.authViewModel
160
+ .auth(password: pin.fold('', (ac, val) => ac + '$val')),
161
false,
162
_pinCodeKey));
163
}
lib/src/screens/auth/create_login_page.dart
+13
-12
@@ -14,15 +14,16 @@ Widget createLoginPage(
14
@required WalletService walletService,
15
@required WalletListService walletListService,
16
@required AuthenticationStore authenticationStore}) =>
17
- Provider(
18
- create: (_) => AuthStore(
19
- sharedPreferences: sharedPreferences,
20
- userService: userService,
21
- walletService: walletService),
22
- child: AuthPage(
23
- onAuthenticationFinished: (isAuthenticated, state) {
24
- if (isAuthenticated) {
25
- authenticationStore.loggedIn();
26
- }
27
- },
28
- closable: false));
17
+ null;
18
+// Provider(
19
+// create: (_) => AuthStore(
20
+// sharedPreferences: sharedPreferences,
21
+// userService: userService,
22
+// walletService: walletService),
23
+// child: AuthPage(
24
+// onAuthenticationFinished: (isAuthenticated, state) {
25
+// if (isAuthenticated) {
26
+// authenticationStore.loggedIn();
27
+// }
28
+// },
29
+// closable: false));
lib/src/screens/auth/create_unlock_page.dart
+11
-10
@@ -11,13 +11,14 @@ Widget createUnlockPage(
11
@required UserService userService,
12
@required WalletService walletService,
13
@required Function(bool, AuthPageState) onAuthenticationFinished}) =>
14
- WillPopScope(
15
- onWillPop: () async => false,
16
- child: Provider(
17
- create: (_) => AuthStore(
18
- sharedPreferences: sharedPreferences,
19
- userService: userService,
20
- walletService: walletService),
21
- child: AuthPage(
22
- onAuthenticationFinished: onAuthenticationFinished,
23
- closable: false)));
\ No newline at end of file
14
+ null;
15
+// WillPopScope(
16
+// onWillPop: () async => false,
17
+// child: Provider(
18
+// create: (_) => AuthStore(
19
+// sharedPreferences: sharedPreferences,
20
+// userService: userService,
21
+// walletService: walletService),
22
+// child: AuthPage(
23
+// onAuthenticationFinished: onAuthenticationFinished,
24
+// closable: false)));
\ No newline at end of file
lib/src/screens/base_page.dart
+18
-13
@@ -10,12 +10,19 @@ enum AppBarStyle { regular, withShadow }
10
11
abstract class BasePage extends StatelessWidget {
12
String get title => null;
13
+
14
bool get isModalBackButton => false;
15
+
16
Color get backgroundLightColor => Colors.white;
17
+
18
Color get backgroundDarkColor => PaletteDark.darkNightBlue;
19
+
20
bool get resizeToAvoidBottomPadding => true;
21
+
22
AppBarStyle get appBarStyle => AppBarStyle.regular;
23
24
+ Widget Function(BuildContext, Widget) get rootWrapper => null;
25
+
26
final _backArrowImage = Image.asset('assets/images/back_arrow.png');
27
final _backArrowImageDarkTheme =
28
Image.asset('assets/images/back_arrow_dark_theme.png');
@@ -83,9 +90,8 @@ abstract class BasePage extends StatelessWidget {
90
leading: leading(context),
91
middle: middle(context),
92
trailing: trailing(context),
86
- backgroundColor: _isDarkTheme
87
- ? backgroundDarkColor
88
- : backgroundLightColor);
93
+ backgroundColor:
94
+ _isDarkTheme ? backgroundDarkColor : backgroundLightColor);
95
96
case AppBarStyle.withShadow:
97
return NavBar.withShadow(
@@ -93,9 +99,8 @@ abstract class BasePage extends StatelessWidget {
99
leading: leading(context),
100
middle: middle(context),
101
trailing: trailing(context),
96
- backgroundColor: _isDarkTheme
97
- ? backgroundDarkColor
98
- : backgroundLightColor);
102
+ backgroundColor:
103
+ _isDarkTheme ? backgroundDarkColor : backgroundLightColor);
104
105
default:
106
return NavBar(
@@ -103,9 +108,8 @@ abstract class BasePage extends StatelessWidget {
108
leading: leading(context),
109
middle: middle(context),
110
trailing: trailing(context),
106
- backgroundColor: _isDarkTheme
107
- ? backgroundDarkColor
108
- : backgroundLightColor);
111
+ backgroundColor:
112
+ _isDarkTheme ? backgroundDarkColor : backgroundLightColor);
113
}
114
}
115
@@ -116,13 +120,14 @@ abstract class BasePage extends StatelessWidget {
120
final _themeChanger = Provider.of<ThemeChanger>(context);
121
final _isDarkTheme = _themeChanger.getTheme() == Themes.darkTheme;
122
119
- return Scaffold(
120
- backgroundColor: _isDarkTheme
121
- ? backgroundDarkColor
122
- : backgroundLightColor,
123
+ final root = Scaffold(
124
+ backgroundColor:
125
+ _isDarkTheme ? backgroundDarkColor : backgroundLightColor,
126
resizeToAvoidBottomPadding: resizeToAvoidBottomPadding,
127
appBar: appBar(context),
128
body: SafeArea(child: body(context)),
129
floatingActionButton: floatingActionButton(context));
130
+
131
+ return rootWrapper?.call(context, root) ?? root;
132
}
133
}
lib/src/screens/dashboard/dashboard_page.dart
+21
-23
@@ -1,28 +1,36 @@
1
import 'package:flutter/material.dart';
2
import 'package:flutter/cupertino.dart';
3
+import 'package:cake_wallet/view_model/dashboard_view_model.dart';
4
import 'package:cake_wallet/src/screens/dashboard/widgets/wallet_card.dart';
5
import 'package:cake_wallet/src/screens/dashboard/widgets/trade_history_panel.dart';
6
import 'package:cake_wallet/src/screens/dashboard/widgets/menu_widget.dart';
7
8
class DashboardPage extends StatelessWidget {
9
+ DashboardPage({@required this.walletViewModel});
10
+
11
+ final DashboardViewModel walletViewModel;
12
final _bodyKey = GlobalKey();
13
14
@override
11
- Widget build(BuildContext context) => DashboardPageBody(key: _bodyKey);
15
+ Widget build(BuildContext context) =>
16
+ DashboardPageBody(key: _bodyKey, walletViewModel: walletViewModel);
17
}
18
19
class DashboardPageBody extends StatefulWidget {
15
- DashboardPageBody({Key key}) : super(key: key);
20
+ DashboardPageBody({Key key, @required this.walletViewModel})
21
+ : super(key: key);
22
+
23
+ final DashboardViewModel walletViewModel;
24
25
@override
26
DashboardPageBodyState createState() => DashboardPageBodyState();
27
}
28
29
class DashboardPageBodyState extends State<DashboardPageBody> {
22
-
30
@override
31
Widget build(BuildContext context) {
25
- final menuButton = Image.asset('assets/images/header.png',
32
+ final menuButton = Image.asset(
33
+ 'assets/images/header.png',
34
color: Theme.of(context).primaryTextTheme.title.color,
35
);
36
@@ -30,15 +38,10 @@ class DashboardPageBodyState extends State<DashboardPageBody> {
38
child: Scaffold(
39
body: Container(
40
decoration: BoxDecoration(
33
- gradient: LinearGradient(
34
- colors: [
35
- Theme.of(context).scaffoldBackgroundColor,
36
- Theme.of(context).primaryColor
37
- ],
38
- begin: Alignment.centerLeft,
39
- end: Alignment.centerRight
40
- )
41
- ),
41
+ gradient: LinearGradient(colors: [
42
+ Theme.of(context).scaffoldBackgroundColor,
43
+ Theme.of(context).primaryColor
44
+ ], begin: Alignment.centerLeft, end: Alignment.centerRight)),
45
child: Column(
46
children: <Widget>[
47
Container(
@@ -55,22 +58,17 @@ class DashboardPageBodyState extends State<DashboardPageBody> {
58
padding: EdgeInsets.all(0),
59
onPressed: () async {
60
await showDialog<void>(
58
- builder: (_) => MenuWidget(),
59
- context: context
60
- );
61
+ builder: (_) => MenuWidget(), context: context);
62
},
63
child: menuButton),
64
),
65
),
66
),
67
Padding(
67
- padding: EdgeInsets.only(left: 20, top: 20),
68
- child: WalletCard(),
69
- ),
70
- SizedBox(
71
- height: 28,
72
- ),
73
- Expanded(child: TradeHistoryPanel())
68
+ padding: EdgeInsets.only(left: 20, top: 20),
69
+ child: WalletCard(walletVM: widget.walletViewModel)),
70
+ SizedBox(height: 28),
71
+ Expanded(child: TradeHistoryPanel(dashboardViewModel: widget.walletViewModel))
72
],
73
),
74
),
lib/src/screens/dashboard/widgets/button_header.dart
+76
-76
@@ -17,7 +17,7 @@ class ButtonHeader extends SliverPersistentHeaderDelegate {
17
18
@override
19
Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) {
20
- final actionListStore = Provider.of<ActionListStore>(context);
20
+// final actionListStore = Provider.of<ActionListStore>(context);
21
final historyPanelWidth = MediaQuery.of(context).size.width;
22
23
final _themeChanger = Provider.of<ThemeChanger>(context);
@@ -97,44 +97,44 @@ class ButtonHeader extends SliverPersistentHeaderDelegate {
97
style: TextStyle(
98
fontWeight: FontWeight.bold,
99
color: Theme.of(context).primaryTextTheme.caption.color))),
100
- PopupMenuItem(
101
- value: 0,
102
- child: Observer(
103
- builder: (_) => Row(
104
- mainAxisAlignment:
105
- MainAxisAlignment
106
- .spaceBetween,
107
- children: [
108
- Text(S.of(context).incoming),
109
- Checkbox(
110
- value: actionListStore
111
- .transactionFilterStore
112
- .displayIncoming,
113
- onChanged: (value) =>
114
- actionListStore
115
- .transactionFilterStore
116
- .toggleIncoming(),
117
- )
118
- ]))),
119
- PopupMenuItem(
120
- value: 1,
121
- child: Observer(
122
- builder: (_) => Row(
123
- mainAxisAlignment:
124
- MainAxisAlignment
125
- .spaceBetween,
126
- children: [
127
- Text(S.of(context).outgoing),
128
- Checkbox(
129
- value: actionListStore
130
- .transactionFilterStore
131
- .displayOutgoing,
132
- onChanged: (value) =>
133
- actionListStore
134
- .transactionFilterStore
135
- .toggleOutgoing(),
136
- )
137
- ]))),
100
+// PopupMenuItem(
101
+// value: 0,
102
+// child: Observer(
103
+// builder: (_) => Row(
104
+// mainAxisAlignment:
105
+// MainAxisAlignment
106
+// .spaceBetween,
107
+// children: [
108
+// Text(S.of(context).incoming),
109
+// Checkbox(
110
+// value: actionListStore
111
+// .transactionFilterStore
112
+// .displayIncoming,
113
+// onChanged: (value) =>
114
+// actionListStore
115
+// .transactionFilterStore
116
+// .toggleIncoming(),
117
+// )
118
+// ]))),
119
+// PopupMenuItem(
120
+// value: 1,
121
+// child: Observer(
122
+// builder: (_) => Row(
123
+// mainAxisAlignment:
124
+// MainAxisAlignment
125
+// .spaceBetween,
126
+// children: [
127
+// Text(S.of(context).outgoing),
128
+// Checkbox(
129
+// value: actionListStore
130
+// .transactionFilterStore
131
+// .displayOutgoing,
132
+// onChanged: (value) =>
133
+// actionListStore
134
+// .transactionFilterStore
135
+// .toggleOutgoing(),
136
+// )
137
+// ]))),
138
PopupMenuItem(
139
value: 2,
140
child:
@@ -156,17 +156,17 @@ class ButtonHeader extends SliverPersistentHeaderDelegate {
156
.spaceBetween,
157
children: [
158
Text('XMR.TO'),
159
- Checkbox(
160
- value: actionListStore
161
- .tradeFilterStore
162
- .displayXMRTO,
163
- onChanged: (value) =>
164
- actionListStore
165
- .tradeFilterStore
166
- .toggleDisplayExchange(
167
- ExchangeProviderDescription
168
- .xmrto),
169
- )
159
+// Checkbox(
160
+// value: actionListStore
161
+// .tradeFilterStore
162
+// .displayXMRTO,
163
+// onChanged: (value) =>
164
+// actionListStore
165
+// .tradeFilterStore
166
+// .toggleDisplayExchange(
167
+// ExchangeProviderDescription
168
+// .xmrto),
169
+// )
170
]))),
171
PopupMenuItem(
172
value: 4,
@@ -177,17 +177,17 @@ class ButtonHeader extends SliverPersistentHeaderDelegate {
177
.spaceBetween,
178
children: [
179
Text('Change.NOW'),
180
- Checkbox(
181
- value: actionListStore
182
- .tradeFilterStore
183
- .displayChangeNow,
184
- onChanged: (value) =>
185
- actionListStore
186
- .tradeFilterStore
187
- .toggleDisplayExchange(
188
- ExchangeProviderDescription
189
- .changeNow),
190
- )
180
+// Checkbox(
181
+// value: actionListStore
182
+// .tradeFilterStore
183
+// .displayChangeNow,
184
+// onChanged: (value) =>
185
+// actionListStore
186
+// .tradeFilterStore
187
+// .toggleDisplayExchange(
188
+// ExchangeProviderDescription
189
+// .changeNow),
190
+// )
191
]))),
192
PopupMenuItem(
193
value: 5,
@@ -198,17 +198,17 @@ class ButtonHeader extends SliverPersistentHeaderDelegate {
198
.spaceBetween,
199
children: [
200
Text('MorphToken'),
201
- Checkbox(
202
- value: actionListStore
203
- .tradeFilterStore
204
- .displayMorphToken,
205
- onChanged: (value) =>
206
- actionListStore
207
- .tradeFilterStore
208
- .toggleDisplayExchange(
209
- ExchangeProviderDescription
210
- .morphToken),
211
- )
201
+// Checkbox(
202
+// value: actionListStore
203
+// .tradeFilterStore
204
+// .displayMorphToken,
205
+// onChanged: (value) =>
206
+// actionListStore
207
+// .tradeFilterStore
208
+// .toggleDisplayExchange(
209
+// ExchangeProviderDescription
210
+// .morphToken),
211
+// )
212
])))
213
],
214
child: filterButton,
@@ -225,10 +225,10 @@ class ButtonHeader extends SliverPersistentHeaderDelegate {
225
.add(Duration(days: 1)));
226
227
if (picked != null && picked.length == 2) {
228
- actionListStore.transactionFilterStore
229
- .changeStartDate(picked.first);
230
- actionListStore.transactionFilterStore
231
- .changeEndDate(picked.last);
228
+// actionListStore.transactionFilterStore
229
+// .changeStartDate(picked.first);
230
+// actionListStore.transactionFilterStore
231
+// .changeEndDate(picked.last);
232
}
233
}
234
},
lib/src/screens/dashboard/widgets/trade_history_panel.dart
+129
-116
@@ -1,21 +1,26 @@
1
-import 'package:cake_wallet/src/domain/common/balance_display_mode.dart';
2
-import 'package:cake_wallet/src/stores/action_list/action_list_store.dart';
3
-import 'package:cake_wallet/src/stores/action_list/date_section_item.dart';
4
-import 'package:cake_wallet/src/stores/action_list/trade_list_item.dart';
5
-import 'package:cake_wallet/src/stores/action_list/transaction_list_item.dart';
6
-import 'package:cake_wallet/src/stores/settings/settings_store.dart';
1
import 'package:flutter/cupertino.dart';
2
import 'package:flutter/material.dart';
3
import 'package:flutter_mobx/flutter_mobx.dart';
4
import 'package:intl/intl.dart';
5
import 'package:provider/provider.dart';
6
import 'package:cake_wallet/routes.dart';
7
+import 'package:cake_wallet/view_model/dashboard_view_model.dart';
8
+import 'package:cake_wallet/src/domain/common/balance_display_mode.dart';
9
+import 'package:cake_wallet/src/stores/action_list/action_list_store.dart';
10
+import 'package:cake_wallet/src/stores/action_list/date_section_item.dart';
11
+import 'package:cake_wallet/src/stores/action_list/trade_list_item.dart';
12
+import 'package:cake_wallet/src/stores/action_list/transaction_list_item.dart';
13
+import 'package:cake_wallet/src/stores/settings/settings_store.dart';
14
import 'date_section_raw.dart';
15
import 'trade_row.dart';
16
import 'transaction_raw.dart';
17
import 'button_header.dart';
18
19
class TradeHistoryPanel extends StatefulWidget {
20
+ TradeHistoryPanel({this.dashboardViewModel});
21
+
22
+ DashboardViewModel dashboardViewModel;
23
+
24
@override
25
TradeHistoryPanelState createState() => TradeHistoryPanelState();
26
}
@@ -44,119 +49,127 @@ class TradeHistoryPanelState extends State<TradeHistoryPanel> {
49
50
@override
51
Widget build(BuildContext context) {
47
- final actionListStore = Provider.of<ActionListStore>(context);
48
- final settingsStore = Provider.of<SettingsStore>(context);
49
- final transactionDateFormat = DateFormat("HH:mm");
52
+// final actionListStore = Provider.of<ActionListStore>(context);
53
+// final settingsStore = Provider.of<SettingsStore>(context);
54
+ final transactionDateFormat = DateFormat('HH:mm');
55
56
return Container(
52
- height: MediaQuery.of(context).size.height,
53
- width: MediaQuery.of(context).size.width,
54
- alignment: Alignment.bottomCenter,
55
- child: AnimatedContainer(
57
+ height: MediaQuery.of(context).size.height,
58
width: MediaQuery.of(context).size.width,
57
- height: panelHeight,
58
- duration: Duration(milliseconds: 1000),
59
- curve: Curves.fastOutSlowIn,
60
- child: ClipRRect(
61
- borderRadius: BorderRadius.only(topLeft: Radius.circular(20), topRight: Radius.circular(20)),
62
- child: CustomScrollView(
63
- slivers: <Widget>[
64
- SliverPersistentHeader(
65
- delegate: ButtonHeader(),
66
- pinned: true,
67
- floating: false,
68
- ),
69
- Observer(
70
- key: _listObserverKey,
71
- builder: (_) {
72
- final items = actionListStore.items == null
73
- ? <String>[]
74
- : actionListStore.items;
75
- final itemsCount = items.length + 1;
76
- final symbol = settingsStore.fiatCurrency.toString();
77
- double freeSpaceHeight = MediaQuery.of(context).size.height - 496;
78
-
79
- return SliverList(
80
- key: _listKey,
81
- delegate: SliverChildBuilderDelegate(
82
- (context, index) {
83
-
84
- if (index == itemsCount - 1) {
85
- freeSpaceHeight = freeSpaceHeight >= 0 ? freeSpaceHeight : 0;
59
+ alignment: Alignment.bottomCenter,
60
+ child: AnimatedContainer(
61
+ width: MediaQuery.of(context).size.width,
62
+ height: panelHeight,
63
+ duration: Duration(milliseconds: 1000),
64
+ curve: Curves.fastOutSlowIn,
65
+ child: ClipRRect(
66
+ borderRadius: BorderRadius.only(
67
+ topLeft: Radius.circular(20),
68
+ topRight: Radius.circular(20)),
69
+ child: CustomScrollView(
70
+ slivers: <Widget>[
71
+ SliverPersistentHeader(
72
+ delegate: ButtonHeader(),
73
+ pinned: true,
74
+ floating: false,
75
+ ),
76
+ Observer(
77
+ key: _listObserverKey,
78
+ builder: (_) {
79
+// final items = actionListStore.items == null
80
+// ? <String>[]
81
+// : actionListStore.items;
82
+ final items = widget.dashboardViewModel.transactions;
83
+ final itemsCount = items.length + 1;
84
+ final symbol =
85
+ '\$'; // settingsStore.fiatCurrency.toString();
86
+ var freeSpaceHeight =
87
+ MediaQuery.of(context).size.height - 496;
88
+
89
+ return SliverList(
90
+ key: _listKey,
91
+ delegate:
92
+ SliverChildBuilderDelegate((context, index) {
93
+ if (index == itemsCount - 1) {
94
+ freeSpaceHeight = freeSpaceHeight >= 0
95
+ ? freeSpaceHeight
96
+ : 0;
97
+
98
+ return Container(
99
+ height: freeSpaceHeight,
100
+ width: MediaQuery.of(context).size.width,
101
+ color: Theme.of(context).backgroundColor);
102
+ }
103
+
104
+ final item = items[index];
105
+
106
+ if (item is DateSectionItem) {
107
+ freeSpaceHeight -= 38;
108
+ return DateSectionRaw(date: item.date);
109
+ }
110
+
111
+ if (item is TransactionListItem) {
112
+ freeSpaceHeight -= 62;
113
+ final transaction = item.transaction;
114
+ final savedDisplayMode =
115
+ BalanceDisplayMode.all;
116
+ //settingsStore
117
+// .balanceDisplayMode;
118
+ final formattedAmount = savedDisplayMode ==
119
+ BalanceDisplayMode.hiddenBalance
120
+ ? '---'
121
+ : transaction.amountFormatted();
122
+ final formattedFiatAmount =
123
+ savedDisplayMode ==
124
+ BalanceDisplayMode.hiddenBalance
125
+ ? '---'
126
+ : transaction
127
+ .fiatAmount(); // symbol ???
128
+
129
+ return TransactionRow(
130
+ onTap: () => Navigator.of(context)
131
+ .pushNamed(Routes.transactionDetails,
132
+ arguments: transaction),
133
+ direction: transaction.direction,
134
+ formattedDate: transactionDateFormat
135
+ .format(transaction.date),
136
+ formattedAmount: formattedAmount,
137
+ formattedFiatAmount: formattedFiatAmount,
138
+ isPending: transaction.isPending);
139
+ }
140
+
141
+ if (item is TradeListItem) {
142
+ freeSpaceHeight -= 62;
143
+ final trade = item.trade;
144
+ final savedDisplayMode =
145
+ BalanceDisplayMode.all;
146
+ //settingsStore
147
+ // .balanceDisplayMode;
148
+ final formattedAmount = trade.amount != null
149
+ ? savedDisplayMode ==
150
+ BalanceDisplayMode.hiddenBalance
151
+ ? '---'
152
+ : trade.amountFormatted()
153
+ : trade.amount;
154
+
155
+ return TradeRow(
156
+ onTap: () => Navigator.of(context)
157
+ .pushNamed(Routes.tradeDetails,
158
+ arguments: trade),
159
+ provider: trade.provider,
160
+ from: trade.from,
161
+ to: trade.to,
162
+ createdAtFormattedDate:
163
+ transactionDateFormat
164
+ .format(trade.createdAt),
165
+ formattedAmount: formattedAmount);
166
+ }
167
168
return Container(
88
- height: freeSpaceHeight,
89
- width: MediaQuery.of(context).size.width,
90
- color: Theme.of(context).backgroundColor,
91
- );
92
- }
93
-
94
- final item = items[index];
95
-
96
- if (item is DateSectionItem) {
97
- freeSpaceHeight -= 38;
98
- return DateSectionRaw(date: item.date);
99
- }
100
-
101
- if (item is TransactionListItem) {
102
- freeSpaceHeight -= 62;
103
- final transaction = item.transaction;
104
- final savedDisplayMode = settingsStore.balanceDisplayMode;
105
- final formattedAmount =
106
- savedDisplayMode == BalanceDisplayMode.hiddenBalance
107
- ? '---'
108
- : transaction.amountFormatted();
109
- final formattedFiatAmount =
110
- savedDisplayMode == BalanceDisplayMode.hiddenBalance
111
- ? '---'
112
- : transaction.fiatAmount(); // symbol ???
113
-
114
- return TransactionRow(
115
- onTap: () => Navigator.of(context).pushNamed(
116
- Routes.transactionDetails,
117
- arguments: transaction),
118
- direction: transaction.direction,
119
- formattedDate:
120
- transactionDateFormat.format(transaction.date),
121
- formattedAmount: formattedAmount,
122
- formattedFiatAmount: formattedFiatAmount,
123
- isPending: transaction.isPending);
124
- }
125
-
126
- if (item is TradeListItem) {
127
- freeSpaceHeight -= 62;
128
- final trade = item.trade;
129
- final savedDisplayMode = settingsStore.balanceDisplayMode;
130
- final formattedAmount = trade.amount != null
131
- ? savedDisplayMode == BalanceDisplayMode.hiddenBalance
132
- ? '---'
133
- : trade.amountFormatted()
134
- : trade.amount;
135
-
136
- return TradeRow(
137
- onTap: () => Navigator.of(context)
138
- .pushNamed(Routes.tradeDetails, arguments: trade),
139
- provider: trade.provider,
140
- from: trade.from,
141
- to: trade.to,
142
- createdAtFormattedDate:
143
- transactionDateFormat.format(trade.createdAt),
144
- formattedAmount: formattedAmount);
145
- }
146
-
147
- return Container(
148
- color: Theme.of(context).backgroundColor
149
- );
150
- },
151
-
152
- childCount: itemsCount
153
- )
154
- );
155
- })
156
- ],
157
- ),
158
- )
159
- ),
160
- );
169
+ color: Theme.of(context).backgroundColor);
170
+ }, childCount: itemsCount));
171
+ })
172
+ ],
173
+ )))); //,
174
}
162
-}
\ No newline at end of file
175
+}
lib/src/screens/dashboard/widgets/wallet_card.dart
+341
-316
@@ -1,21 +1,27 @@
1
import 'dart:async';
2
import 'package:cake_wallet/palette.dart';
3
-import 'package:cake_wallet/src/domain/common/balance_display_mode.dart';
3
+import 'package:cake_wallet/src/domain/common/wallet_type.dart';
4
import 'package:flutter/services.dart';
5
import 'package:provider/provider.dart';
6
+import 'package:flutter/cupertino.dart';
7
+import 'package:flutter/material.dart';
8
+import 'package:flutter_mobx/flutter_mobx.dart';
9
+import 'package:cake_wallet/src/domain/common/balance_display_mode.dart';
10
import 'package:cake_wallet/src/stores/balance/balance_store.dart';
11
import 'package:cake_wallet/src/stores/settings/settings_store.dart';
12
import 'package:cake_wallet/src/stores/sync/sync_store.dart';
13
import 'package:cake_wallet/src/stores/wallet/wallet_store.dart';
10
-import 'package:flutter/cupertino.dart';
11
-import 'package:flutter/material.dart';
12
-import 'package:flutter_mobx/flutter_mobx.dart';
14
import 'package:cake_wallet/generated/i18n.dart';
15
import 'package:cake_wallet/src/domain/common/sync_status.dart';
16
import 'package:cake_wallet/src/screens/receive/widgets/qr_image.dart';
17
import 'package:cake_wallet/routes.dart';
18
+import 'package:cake_wallet/view_model/dashboard_view_model.dart';
19
20
class WalletCard extends StatefulWidget {
21
+ WalletCard({this.walletVM});
22
+
23
+ DashboardViewModel walletVM;
24
+
25
@override
26
WalletCardState createState() => WalletCardState();
27
}
@@ -50,14 +56,12 @@ class WalletCardState extends State<WalletCard> {
56
cardWidth = screenWidth;
57
opacity = 1;
58
});
53
- Timer(Duration(milliseconds: 500), () =>
54
- setState(() => isDraw = true)
55
- );
59
+ Timer(Duration(milliseconds: 500), () => setState(() => isDraw = true));
60
}
61
62
@override
63
Widget build(BuildContext context) {
60
- final List<Color> colorsSync = [
64
+ final colorsSync = [
65
Theme.of(context).cardTheme.color,
66
Theme.of(context).hoverColor
67
];
@@ -67,77 +71,67 @@ class WalletCardState extends State<WalletCard> {
71
height: cardHeight,
72
alignment: Alignment.centerRight,
73
child: AnimatedContainer(
70
- alignment: Alignment.centerLeft,
71
- width: cardWidth,
72
- height: cardHeight,
73
- duration: Duration(milliseconds: 500),
74
- curve: Curves.fastOutSlowIn,
75
- padding: EdgeInsets.only(
76
- top: 1,
77
- left: 1,
78
- bottom: 1
79
- ),
80
- decoration: BoxDecoration(
81
- borderRadius: BorderRadius.only(topLeft: Radius.circular(10), bottomLeft: Radius.circular(10)),
82
- color: Theme.of(context).focusColor,
83
- boxShadow: [
84
- BoxShadow(
85
- color: PaletteDark.darkNightBlue.withOpacity(0.5),
86
- blurRadius: 8,
87
- offset: Offset(5, 5))
88
- ]
89
- ),
90
- child: ClipRRect(
91
- borderRadius: BorderRadius.only(topLeft: Radius.circular(10), bottomLeft: Radius.circular(10)),
92
- child: Container(
93
- width: cardWidth,
94
- height: cardHeight,
95
- color: Theme.of(context).cardColor,
96
- child: InkWell(
97
- onTap: () => setState(() => isFrontSide = !isFrontSide),
98
- child: isFrontSide
99
- ? frontSide(colorsSync)
100
- : backSide(colorsSync)
74
+ alignment: Alignment.centerLeft,
75
+ width: cardWidth,
76
+ height: cardHeight,
77
+ duration: Duration(milliseconds: 500),
78
+ curve: Curves.fastOutSlowIn,
79
+ padding: EdgeInsets.only(top: 1, left: 1, bottom: 1),
80
+ decoration: BoxDecoration(
81
+ borderRadius: BorderRadius.only(
82
+ topLeft: Radius.circular(10),
83
+ bottomLeft: Radius.circular(10)),
84
+ color: Theme.of(context).focusColor,
85
+ boxShadow: [
86
+ BoxShadow(
87
+ color: PaletteDark.darkNightBlue.withOpacity(0.5),
88
+ blurRadius: 8,
89
+ offset: Offset(5, 5))
90
+ ]),
91
+ child: ClipRRect(
92
+ borderRadius: BorderRadius.only(
93
+ topLeft: Radius.circular(10), bottomLeft: Radius.circular(10)),
94
+ child: Container(
95
+ width: cardWidth,
96
+ height: cardHeight,
97
+ color: Theme.of(context).cardColor,
98
+ child: InkWell(
99
+ onTap: () => setState(() => isFrontSide = !isFrontSide),
100
+ child: isFrontSide
101
+ ? frontSide(colorsSync)
102
+ : backSide(colorsSync)),
103
),
102
- ),
103
- )
104
- ),
104
+ )),
105
);
106
}
107
108
Widget frontSide(List<Color> colorsSync) {
109
- final syncStore = Provider.of<SyncStore>(context);
110
- final walletStore = Provider.of<WalletStore>(context);
109
+// final syncStore = Provider.of<SyncStore>(context);
110
+// final walletStore = Provider.of<WalletStore>(context);
111
final settingsStore = Provider.of<SettingsStore>(context);
112
- final balanceStore = Provider.of<BalanceStore>(context);
113
- final triangleButton = Image.asset('assets/images/triangle.png',
112
+// final balanceStore = Provider.of<BalanceStore>(context);
113
+ final triangleButton = Image.asset(
114
+ 'assets/images/triangle.png',
115
color: Theme.of(context).primaryTextTheme.title.color,
116
);
117
118
return Observer(
119
key: _syncingObserverKey,
120
builder: (_) {
120
- final status = syncStore.status;
121
+ final status = widget.walletVM.status;
122
final statusText = status.title();
122
- final progress = syncStore.status.progress();
123
+ final progress = status.progress();
124
final indicatorWidth = progress * cardWidth;
124
-
125
- String shortAddress = walletStore.subaddress.address;
126
- shortAddress = shortAddress.replaceRange(4, shortAddress.length - 4, '...');
127
-
125
+ final shortAddress = widget.walletVM.address
126
+ .replaceRange(4, widget.walletVM.address.length - 4, '...');
127
var descriptionText = '';
128
129
if (status is SyncingSyncStatus) {
131
- descriptionText = S
132
- .of(context)
133
- .Blocks_remaining(
134
- syncStore.status.toString());
130
+ descriptionText = S.of(context).Blocks_remaining(status.toString());
131
}
132
133
if (status is FailedSyncStatus) {
138
- descriptionText = S
139
- .of(context)
140
- .please_try_to_connect_to_another_node;
134
+ descriptionText = S.of(context).please_try_to_connect_to_another_node;
135
}
136
137
return Container(
@@ -149,183 +143,206 @@ class WalletCardState extends State<WalletCard> {
143
height: cardHeight,
144
width: indicatorWidth,
145
decoration: BoxDecoration(
152
- borderRadius: BorderRadius.only(topLeft: Radius.circular(10), bottomLeft: Radius.circular(10)),
146
+ borderRadius: BorderRadius.only(
147
+ topLeft: Radius.circular(10),
148
+ bottomLeft: Radius.circular(10)),
149
gradient: LinearGradient(
150
colors: colorsSync,
151
begin: Alignment.topCenter,
156
- end: Alignment.bottomCenter
157
- )
158
- ),
152
+ end: Alignment.bottomCenter)),
153
),
154
progress != 1
161
- ? Positioned(
162
- left: indicatorWidth,
163
- top: 0,
164
- bottom: 0,
165
- child: Container(
166
- width: 1,
167
- height: cardHeight,
168
- color: Theme.of(context).focusColor,
169
- )
170
- )
171
- : Offstage(),
172
- isDraw ? Positioned(
173
- left: 20,
174
- right: 20,
175
- top: 30,
176
- bottom: 30,
177
- child: Container(
178
- child: Column(
179
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
180
- children: <Widget>[
181
- Row(
182
- crossAxisAlignment: CrossAxisAlignment.start,
155
+ ? Positioned(
156
+ left: indicatorWidth,
157
+ top: 0,
158
+ bottom: 0,
159
+ child: Container(
160
+ width: 1,
161
+ height: cardHeight,
162
+ color: Theme.of(context).focusColor,
163
+ ))
164
+ : Offstage(),
165
+ isDraw
166
+ ? Positioned(
167
+ left: 20,
168
+ right: 20,
169
+ top: 30,
170
+ bottom: 30,
171
+ child: Container(
172
+ child: Column(
173
mainAxisAlignment: MainAxisAlignment.spaceBetween,
174
children: <Widget>[
185
- Column(
175
+ Row(
176
crossAxisAlignment: CrossAxisAlignment.start,
177
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
178
children: <Widget>[
188
- InkWell(
189
- onTap: () {},
190
- child: Row(
191
- children: <Widget>[
179
+ Column(
180
+ crossAxisAlignment: CrossAxisAlignment.start,
181
+ children: <Widget>[
182
+ InkWell(
183
+ onTap: () {},
184
+ child: Row(
185
+ children: <Widget>[
186
+ Text(
187
+ widget.walletVM.name,
188
+ style: TextStyle(
189
+ fontSize: 20,
190
+ fontWeight: FontWeight.bold,
191
+ color: Theme.of(context)
192
+ .primaryTextTheme
193
+ .title
194
+ .color),
195
+ ),
196
+ SizedBox(width: 10),
197
+ triangleButton
198
+ ],
199
+ ),
200
+ ),
201
+ SizedBox(height: 5),
202
+ if (widget.walletVM.subname?.isNotEmpty ?? false)
203
Text(
193
- walletStore.name,
204
+ widget.walletVM.subname,
205
style: TextStyle(
195
- fontSize: 20,
196
- fontWeight: FontWeight.bold,
197
- color: Theme.of(context).primaryTextTheme.title.color
198
- ),
199
- ),
200
- SizedBox(width: 10),
201
- triangleButton
202
- ],
203
- ),
204
- ),
205
- SizedBox(
206
- height: 5,
206
+ fontSize: 12,
207
+ color: Theme.of(context)
208
+ .primaryTextTheme
209
+ .caption
210
+ .color),
211
+ )
212
+ ],
213
),
208
- Text(
209
- walletStore.account.label,
210
- style: TextStyle(
211
- fontSize: 12,
212
- color: Theme.of(context).primaryTextTheme.caption.color
214
+ Container(
215
+ width: 98,
216
+ height: 32,
217
+ alignment: Alignment.center,
218
+ decoration: BoxDecoration(
219
+ color: Theme.of(context)
220
+ .accentTextTheme
221
+ .subtitle
222
+ .backgroundColor,
223
+ borderRadius: BorderRadius.all(
224
+ Radius.circular(16))),
225
+ child: Text(
226
+ shortAddress,
227
+ style: TextStyle(
228
+ fontSize: 12,
229
+ color: Theme.of(context)
230
+ .primaryTextTheme
231
+ .caption
232
+ .color),
233
),
234
)
235
],
236
),
217
- Container(
218
- width: 98,
219
- height: 32,
220
- alignment: Alignment.center,
221
- decoration: BoxDecoration(
222
- color: Theme.of(context).accentTextTheme.subtitle.backgroundColor,
223
- borderRadius: BorderRadius.all(Radius.circular(16))
224
- ),
225
- child: Text(
226
- shortAddress,
227
- style: TextStyle(
228
- fontSize: 12,
229
- color: Theme.of(context).primaryTextTheme.caption.color
230
- ),
231
- ),
232
- )
233
- ],
234
- ),
235
- status is SyncedSyncStatus
236
- ? Observer(
237
- key: _balanceObserverKey,
238
- builder: (_) {
239
- final balanceDisplayMode = settingsStore.balanceDisplayMode;
240
- final symbol = settingsStore
241
- .fiatCurrency
242
- .toString();
243
- var balance = '---';
244
- var fiatBalance = '---';
237
+ status is SyncedSyncStatus
238
+ ? Observer(
239
+ key: _balanceObserverKey,
240
+ builder: (_) {
241
+ final balanceDisplayMode =
242
+ BalanceDisplayMode.fullBalance;
243
+// settingsStore.balanceDisplayMode;
244
+ final symbol =
245
+ settingsStore.fiatCurrency.toString();
246
+ var balance = '---';
247
+ var fiatBalance = '---';
248
246
- if (balanceDisplayMode ==
247
- BalanceDisplayMode.availableBalance) {
248
- balance =
249
- balanceStore.unlockedBalance ??
250
- '0.0';
251
- fiatBalance =
252
- '$symbol ${balanceStore.fiatUnlockedBalance}';
253
- }
249
+ if (balanceDisplayMode ==
250
+ BalanceDisplayMode.availableBalance) {
251
+ balance = widget.walletVM.balance
252
+ .unlockedBalance ??
253
+ '0.0';
254
+ fiatBalance = '\$ 123.43';
255
+// '$symbol ${balanceStore.fiatUnlockedBalance}';
256
+ }
257
255
- if (balanceDisplayMode ==
256
- BalanceDisplayMode.fullBalance) {
257
- balance =
258
- balanceStore.fullBalance ?? '0.0';
259
- fiatBalance =
260
- '$symbol ${balanceStore.fiatFullBalance}';
261
- }
258
+ if (balanceDisplayMode ==
259
+ BalanceDisplayMode.fullBalance) {
260
+ balance = widget.walletVM.balance
261
+ .totalBalance ??
262
+ '0.0';
263
+ fiatBalance = '\$ 123.43';
264
+// '$symbol ${balanceStore.fiatFullBalance}';
265
+ }
266
263
- return Row(
264
- crossAxisAlignment: CrossAxisAlignment.end,
265
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
266
- children: <Widget>[
267
- Column(
268
- crossAxisAlignment: CrossAxisAlignment.start,
267
+ return Row(
268
+ crossAxisAlignment:
269
+ CrossAxisAlignment.end,
270
+ mainAxisAlignment:
271
+ MainAxisAlignment.spaceBetween,
272
+ children: <Widget>[
273
+ Column(
274
+ crossAxisAlignment:
275
+ CrossAxisAlignment.start,
276
+ children: <Widget>[
277
+ Text(
278
+ balanceDisplayMode.toString(),
279
+ style: TextStyle(
280
+ fontSize: 12,
281
+ color: Theme.of(context)
282
+ .primaryTextTheme
283
+ .caption
284
+ .color),
285
+ ),
286
+ SizedBox(height: 5),
287
+ Text(
288
+ balance,
289
+ style: TextStyle(
290
+ fontSize: 28,
291
+ color: Theme.of(context)
292
+ .primaryTextTheme
293
+ .title
294
+ .color),
295
+ )
296
+ ],
297
+ ),
298
+ Text(
299
+ fiatBalance,
300
+ style: TextStyle(
301
+ fontSize: 14,
302
+ color: Theme.of(context)
303
+ .primaryTextTheme
304
+ .title
305
+ .color),
306
+ )
307
+ ],
308
+ );
309
+ })
310
+ : Row(
311
+ crossAxisAlignment: CrossAxisAlignment.end,
312
+ mainAxisAlignment:
313
+ MainAxisAlignment.spaceBetween,
314
children: <Widget>[
270
- Text(
271
- balanceDisplayMode.toString(),
272
- style: TextStyle(
273
- fontSize: 12,
274
- color: Theme.of(context).primaryTextTheme.caption.color
275
- ),
276
- ),
277
- SizedBox(height: 5),
278
- Text(
279
- balance,
280
- style: TextStyle(
281
- fontSize: 28,
282
- color: Theme.of(context).primaryTextTheme.title.color
283
- ),
315
+ Column(
316
+ crossAxisAlignment:
317
+ CrossAxisAlignment.start,
318
+ children: <Widget>[
319
+ Text(
320
+ statusText,
321
+ style: TextStyle(
322
+ fontSize: 12,
323
+ color: Theme.of(context)
324
+ .primaryTextTheme
325
+ .caption
326
+ .color),
327
+ ),
328
+ SizedBox(height: 5),
329
+ Text(
330
+ descriptionText,
331
+ style: TextStyle(
332
+ fontSize: 14,
333
+ color: Theme.of(context)
334
+ .primaryTextTheme
335
+ .title
336
+ .color),
337
+ )
338
+ ],
339
)
340
],
286
- ),
287
- Text(
288
- fiatBalance,
289
- style: TextStyle(
290
- fontSize: 14,
291
- color: Theme.of(context).primaryTextTheme.title.color
292
- ),
341
)
294
- ],
295
- );
296
- }
297
- )
298
- : Row(
299
- crossAxisAlignment: CrossAxisAlignment.end,
300
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
301
- children: <Widget>[
302
- Column(
303
- crossAxisAlignment: CrossAxisAlignment.start,
304
- children: <Widget>[
305
- Text(
306
- statusText,
307
- style: TextStyle(
308
- fontSize: 12,
309
- color: Theme.of(context).primaryTextTheme.caption.color
310
- ),
311
- ),
312
- SizedBox(height: 5),
313
- Text(
314
- descriptionText,
315
- style: TextStyle(
316
- fontSize: 14,
317
- color: Theme.of(context).primaryTextTheme.title.color
318
- ),
319
- )
320
- ],
321
- )
342
],
323
- )
324
- ],
325
- ),
326
- )
327
- )
328
- : Offstage()
343
+ ),
344
+ ))
345
+ : Offstage()
346
],
347
),
348
);
@@ -334,149 +351,157 @@ class WalletCardState extends State<WalletCard> {
351
}
352
353
Widget backSide(List<Color> colorsSync) {
337
- final walletStore = Provider.of<WalletStore>(context);
338
- final rightArrow = Image.asset('assets/images/right_arrow.png',
354
+ final rightArrow = Image.asset(
355
+ 'assets/images/right_arrow.png',
356
color: Theme.of(context).primaryTextTheme.title.color,
357
);
341
- double messageBoxHeight = 0;
342
- double messageBoxWidth = cardWidth - 10;
358
+ var messageBoxHeight = 0.0;
359
+ var messageBoxWidth = cardWidth - 10;
360
361
return Observer(
345
- key: _addressObserverKey,
346
- builder: (_) {
347
- return Container(
348
- width: cardWidth,
349
- height: cardHeight,
350
- alignment: Alignment.topCenter,
351
- child: Stack(
352
- alignment: Alignment.topRight,
353
- children: <Widget>[
354
- Container(
355
- width: cardWidth,
356
- height: cardHeight,
357
- padding: EdgeInsets.only(left: 20, right: 20, top: 30, bottom: 30),
358
- decoration: BoxDecoration(
359
- borderRadius: BorderRadius.only(topLeft: Radius.circular(10), bottomLeft: Radius.circular(10)),
360
- gradient: LinearGradient(
361
- colors: colorsSync,
362
- begin: Alignment.topCenter,
363
- end: Alignment.bottomCenter
364
- )
365
- ),
366
- child: Column(
367
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
368
- children: <Widget>[
369
- Row(
370
- children: <Widget>[
371
- Expanded(
372
- child: Container(
362
+ key: _addressObserverKey,
363
+ builder: (_) {
364
+ return Container(
365
+ width: cardWidth,
366
+ height: cardHeight,
367
+ alignment: Alignment.topCenter,
368
+ child: Stack(
369
+ alignment: Alignment.topRight,
370
+ children: <Widget>[
371
+ Container(
372
+ width: cardWidth,
373
+ height: cardHeight,
374
+ padding:
375
+ EdgeInsets.only(left: 20, right: 20, top: 30, bottom: 30),
376
+ decoration: BoxDecoration(
377
+ borderRadius: BorderRadius.only(
378
+ topLeft: Radius.circular(10),
379
+ bottomLeft: Radius.circular(10)),
380
+ gradient: LinearGradient(
381
+ colors: colorsSync,
382
+ begin: Alignment.topCenter,
383
+ end: Alignment.bottomCenter)),
384
+ child: Column(
385
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
386
+ children: <Widget>[
387
+ Row(
388
+ children: <Widget>[
389
+ Expanded(
390
+ child: Container(
391
height: 90,
392
child: Column(
375
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
393
crossAxisAlignment: CrossAxisAlignment.start,
394
children: <Widget>[
395
Text(
396
S.current.card_address,
397
style: TextStyle(
398
fontSize: 12,
382
- color: Theme.of(context).primaryTextTheme.caption.color
383
- ),
399
+ color: Theme.of(context)
400
+ .primaryTextTheme
401
+ .caption
402
+ .color),
403
),
404
+ SizedBox(height: 10),
405
GestureDetector(
406
onTap: () {
407
Clipboard.setData(ClipboardData(
388
- text: walletStore.subaddress.address));
389
- _addressObserverKey.currentState.setState(() {
408
+ text: widget.walletVM.address));
409
+ _addressObserverKey.currentState
410
+ .setState(() {
411
messageBoxHeight = 20;
412
messageBoxWidth = cardWidth;
413
});
414
Timer(Duration(milliseconds: 1000), () {
415
try {
395
- _addressObserverKey.currentState.setState(() {
416
+ _addressObserverKey.currentState
417
+ .setState(() {
418
messageBoxHeight = 0;
419
messageBoxWidth = cardWidth - 10;
420
});
399
- } catch(e) {
421
+ } catch (e) {
422
print('${e.toString()}');
423
}
424
});
425
},
426
child: Text(
405
- walletStore.subaddress.address,
427
+ widget.walletVM.address,
428
style: TextStyle(
429
fontSize: 14,
408
- color: Theme.of(context).primaryTextTheme.title.color
409
- ),
430
+ color: Theme.of(context)
431
+ .primaryTextTheme
432
+ .title
433
+ .color),
434
),
435
)
436
],
437
),
438
+ )),
439
+ SizedBox(width: 10),
440
+ Container(
441
+ width: 90,
442
+ height: 90,
443
+ child: QrImage(
444
+ data: widget.walletVM.address,
445
+ backgroundColor: Colors.transparent,
446
+ foregroundColor: Theme.of(context)
447
+ .primaryTextTheme
448
+ .caption
449
+ .color),
450
)
415
- ),
416
- SizedBox(width: 10),
417
- Container(
418
- width: 90,
419
- height: 90,
420
- child: QrImage(
421
- data: walletStore.subaddress.address,
422
- backgroundColor: Colors.transparent,
423
- foregroundColor: Theme.of(context).primaryTextTheme.caption.color
424
- ),
425
- )
426
- ],
427
- ),
428
- Container(
429
- height: 44,
430
- padding: EdgeInsets.only(left: 20, right: 20),
431
- alignment: Alignment.center,
432
- decoration: BoxDecoration(
433
- borderRadius: BorderRadius.all(Radius.circular(22)),
434
- color: Theme.of(context).primaryTextTheme.overline.color
451
+ ],
452
),
436
- child: InkWell(
437
- onTap: () => Navigator.of(context,
438
- rootNavigator: true)
439
- .pushNamed(Routes.receive),
440
- child: Row(
441
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
442
- children: <Widget>[
443
- Text(
444
- S.of(context).accounts_subaddresses,
445
- style: TextStyle(
446
- fontSize: 14,
447
- color: Theme.of(context).primaryTextTheme.title.color
453
+ Container(
454
+ height: 44,
455
+ padding: EdgeInsets.only(left: 20, right: 20),
456
+ alignment: Alignment.center,
457
+ decoration: BoxDecoration(
458
+ borderRadius: BorderRadius.all(Radius.circular(22)),
459
+ color: Theme.of(context)
460
+ .primaryTextTheme
461
+ .overline
462
+ .color),
463
+ child: InkWell(
464
+ onTap: () =>
465
+ Navigator.of(context, rootNavigator: true)
466
+ .pushNamed(Routes.receive),
467
+ child: Row(
468
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
469
+ children: <Widget>[
470
+ Text(
471
+ S.of(context).accounts_subaddresses,
472
+ style: TextStyle(
473
+ fontSize: 14,
474
+ color: Theme.of(context)
475
+ .primaryTextTheme
476
+ .title
477
+ .color),
478
),
449
- ),
450
- rightArrow
451
- ],
479
+ rightArrow
480
+ ],
481
+ ),
482
),
453
- ),
454
- )
455
- ],
456
- ),
457
- ),
458
- AnimatedContainer(
459
- width: messageBoxWidth,
460
- height: messageBoxHeight,
461
- alignment: Alignment.center,
462
- duration: Duration(milliseconds: 500),
463
- curve: Curves.fastOutSlowIn,
464
- decoration: BoxDecoration(
465
- borderRadius: BorderRadius.only(topLeft: Radius.circular(10)),
466
- color: Colors.green
467
- ),
468
- child: Text(
469
- S.of(context).copied_to_clipboard,
470
- style: TextStyle(
471
- fontSize: 10,
472
- color: Colors.white
483
+ )
484
+ ],
485
),
486
),
475
- )
476
- ],
477
- ),
478
- );
479
- }
480
- );
487
+ AnimatedContainer(
488
+ width: messageBoxWidth,
489
+ height: messageBoxHeight,
490
+ alignment: Alignment.center,
491
+ duration: Duration(milliseconds: 500),
492
+ curve: Curves.fastOutSlowIn,
493
+ decoration: BoxDecoration(
494
+ borderRadius:
495
+ BorderRadius.only(topLeft: Radius.circular(10)),
496
+ color: Colors.green),
497
+ child: Text(
498
+ S.of(context).copied_to_clipboard,
499
+ style: TextStyle(fontSize: 10, color: Colors.white),
500
+ ),
501
+ )
502
+ ],
503
+ ),
504
+ );
505
+ });
506
}
482
-}
\ No newline at end of file
507
+}
lib/src/screens/new_wallet/new_wallet_page.dart
+75
-120
@@ -1,85 +1,54 @@
1
-import 'package:cake_wallet/core/monero_wallet_list_service.dart';
2
-import 'package:cake_wallet/core/wallet_creation_service.dart';
3
-import 'package:cake_wallet/core/wallet_credentials.dart';
4
-import 'package:cake_wallet/src/domain/common/wallet_type.dart';
1
import 'package:mobx/mobx.dart';
6
-import 'package:provider/provider.dart';
7
-import 'package:shared_preferences/shared_preferences.dart';
2
import 'package:flutter_mobx/flutter_mobx.dart';
3
import 'package:flutter/material.dart';
4
import 'package:flutter/cupertino.dart';
5
import 'package:cake_wallet/generated/i18n.dart';
12
-import 'package:cake_wallet/src/stores/wallet_creation/wallet_creation_store.dart';
13
-import 'package:cake_wallet/src/stores/wallet_creation/wallet_creation_state.dart';
14
-import 'package:cake_wallet/src/domain/services/wallet_list_service.dart';
15
-import 'package:cake_wallet/src/domain/services/wallet_service.dart';
6
+import 'package:cake_wallet/core/validator.dart';
7
+import 'package:cake_wallet/src/widgets/seed_language_selector.dart';
8
import 'package:cake_wallet/src/screens/base_page.dart';
9
import 'package:cake_wallet/src/widgets/primary_button.dart';
10
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
19
-import 'package:cake_wallet/src/stores/seed_language/seed_language_store.dart';
20
-import 'package:cake_wallet/src/screens/new_wallet/widgets/select_button.dart';
11
import 'package:cake_wallet/src/screens/seed_language/widgets/seed_language_picker.dart';
12
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
13
+import 'package:cake_wallet/view_model/wallet_creation_state.dart';
14
+import 'package:cake_wallet/view_model/wallet_new_vm.dart';
15
16
class NewWalletPage extends BasePage {
25
- NewWalletPage(
26
- {@required this.walletsService,
27
- @required this.walletService,
28
- @required this.sharedPreferences});
17
+ NewWalletPage(this._walletNewVM);
18
30
- final WalletListService walletsService;
31
- final WalletService walletService;
32
- final SharedPreferences sharedPreferences;
19
+ final WalletNewVM _walletNewVM;
20
21
@override
22
String get title => S.current.new_wallet;
23
24
@override
38
- Widget body(BuildContext context) => WalletNameForm();
25
+ Widget body(BuildContext context) => WalletNameForm(_walletNewVM);
26
}
27
28
class WalletNameForm extends StatefulWidget {
29
+ WalletNameForm(this._walletNewVM);
30
+
31
+ final WalletNewVM _walletNewVM;
32
+
33
@override
43
- _WalletNameFormState createState() => _WalletNameFormState();
34
+ _WalletNameFormState createState() => _WalletNameFormState(_walletNewVM);
35
}
36
37
class _WalletNameFormState extends State<WalletNameForm> {
38
+ _WalletNameFormState(this._walletNewVM);
39
+
40
static const aspectRatioImage = 1.22;
41
49
- final _formKey = GlobalKey<FormState>();
50
- final nameController = TextEditingController();
42
final walletNameImage = Image.asset('assets/images/wallet_name.png');
43
+ final _formKey = GlobalKey<FormState>();
44
+ final _languageSelectorKey = GlobalKey<SeedLanguageSelectorState>();
45
+ ReactionDisposer _stateReaction;
46
+ final WalletNewVM _walletNewVM;
47
48
@override
54
- void dispose() {
55
- nameController.dispose();
56
- super.dispose();
57
- }
58
-
59
- @override
60
- Widget build(BuildContext context) {
61
- final walletCreationStore = Provider.of<WalletCreationStore>(context);
62
- final walletCreationService = Provider.of<WalletCreationService>(context);
63
-
64
- // FIXME: Does seed language store is really needed ???
65
-
66
- final seedLanguageStore = Provider.of<SeedLanguageStore>(context);
67
-
68
- final seedLocales = [
69
- S.current.seed_language_english,
70
- S.current.seed_language_chinese,
71
- S.current.seed_language_dutch,
72
- S.current.seed_language_german,
73
- S.current.seed_language_japanese,
74
- S.current.seed_language_portuguese,
75
- S.current.seed_language_russian,
76
- S.current.seed_language_spanish
77
- ];
78
-
79
- nameController.addListener(() =>
80
- walletCreationStore.setDisabledStatus(!nameController.text.isNotEmpty));
81
-
82
- reaction((_) => walletCreationStore.state, (WalletCreationState state) {
49
+ void initState() {
50
+ _stateReaction ??=
51
+ reaction((_) => _walletNewVM.state, (WalletCreationState state) {
52
if (state is WalletCreatedSuccessfully) {
53
Navigator.of(context).popUntil((route) => route.isFirst);
54
}
@@ -98,7 +67,11 @@ class _WalletNameFormState extends State<WalletNameForm> {
67
});
68
}
69
});
70
+ super.initState();
71
+ }
72
73
+ @override
74
+ Widget build(BuildContext context) {
75
return Container(
76
padding: EdgeInsets.only(top: 24),
77
child: ScrollableWithBottomSection(
@@ -116,94 +89,76 @@ class _WalletNameFormState extends State<WalletNameForm> {
89
child: Form(
90
key: _formKey,
91
child: TextFormField(
119
- textAlign: TextAlign.center,
120
- style: TextStyle(
121
- fontSize: 20.0,
122
- fontWeight: FontWeight.w600,
123
- color: Theme.of(context).primaryTextTheme.title.color),
124
- controller: nameController,
125
- decoration: InputDecoration(
126
- hintStyle: TextStyle(
127
- fontSize: 16.0,
128
- color: Theme.of(context)
129
- .primaryTextTheme
130
- .caption
131
- .color),
132
- hintText: S.of(context).wallet_name,
133
- focusedBorder: UnderlineInputBorder(
134
- borderSide: BorderSide(
135
- color: Theme.of(context).dividerColor,
136
- width: 1.0)),
137
- enabledBorder: UnderlineInputBorder(
138
- borderSide: BorderSide(
139
- color: Theme.of(context).dividerColor,
140
- width: 1.0))),
141
- validator: (value) {
142
- walletCreationStore.validateWalletName(value);
143
- return walletCreationStore.errorMessage;
144
- },
145
- )),
92
+ onChanged: (value) => _walletNewVM.name = value,
93
+ textAlign: TextAlign.center,
94
+ style: TextStyle(
95
+ fontSize: 20.0,
96
+ fontWeight: FontWeight.w600,
97
+ color:
98
+ Theme.of(context).primaryTextTheme.title.color),
99
+ decoration: InputDecoration(
100
+ hintStyle: TextStyle(
101
+ fontSize: 16.0,
102
+ color: Theme.of(context)
103
+ .primaryTextTheme
104
+ .caption
105
+ .color),
106
+ hintText: S.of(context).wallet_name,
107
+ focusedBorder: UnderlineInputBorder(
108
+ borderSide: BorderSide(
109
+ color: Theme.of(context).dividerColor,
110
+ width: 1.0)),
111
+ enabledBorder: UnderlineInputBorder(
112
+ borderSide: BorderSide(
113
+ color: Theme.of(context).dividerColor,
114
+ width: 1.0))),
115
+ validator: WalletNameValidator())),
116
),
147
- Padding(
148
- padding: EdgeInsets.only(top: 40),
149
- child: Text(
150
- S.of(context).seed_language_choose,
151
- textAlign: TextAlign.center,
152
- style: TextStyle(
153
- fontSize: 16.0,
154
- fontWeight: FontWeight.w600,
155
- color: Theme.of(context).primaryTextTheme.title.color),
117
+ if (_walletNewVM.hasLanguageSelector) ...[
118
+ Padding(
119
+ padding: EdgeInsets.only(top: 40),
120
+ child: Text(
121
+ S.of(context).seed_language_choose,
122
+ textAlign: TextAlign.center,
123
+ style: TextStyle(
124
+ fontSize: 16.0,
125
+ fontWeight: FontWeight.w600,
126
+ color: Theme.of(context).primaryTextTheme.title.color),
127
+ ),
128
),
157
- ),
158
- Padding(
159
- padding: EdgeInsets.only(top: 24),
160
- child: Observer(
161
- builder: (_) => SelectButton(
162
- image: null,
163
- text: seedLocales[seedLanguages
164
- .indexOf(seedLanguageStore.selectedSeedLanguage)],
165
- color: Theme.of(context)
166
- .accentTextTheme
167
- .title
168
- .backgroundColor,
169
- textColor: Theme.of(context).primaryTextTheme.title.color,
170
- onTap: () async => await showDialog(
171
- context: context,
172
- builder: (BuildContext context) =>
173
- SeedLanguagePicker()))),
174
- )
129
+ Padding(
130
+ padding: EdgeInsets.only(top: 24),
131
+ child: SeedLanguageSelector(
132
+ key: _languageSelectorKey,
133
+ initialSelected: defaultSeedLanguage),
134
+ )
135
+ ]
136
]),
137
bottomSectionPadding:
138
EdgeInsets.only(left: 24, right: 24, bottom: 24),
139
bottomSection: Observer(
140
builder: (context) {
141
return LoadingPrimaryButton(
181
- onPressed: () => _confirmForm(walletCreationService,
182
- seedLanguageStore.selectedSeedLanguage),
142
+ onPressed: _confirmForm,
143
text: S.of(context).continue_text,
144
color: Colors.green,
145
textColor: Colors.white,
186
- isLoading: walletCreationStore.state is WalletIsCreating,
187
- isDisabled: walletCreationStore.isDisabledStatus,
146
+ isLoading: _walletNewVM.state is WalletCreatedSuccessfully,
147
+ isDisabled: _walletNewVM.name.isEmpty,
148
);
149
},
150
)),
151
);
152
}
153
194
- void _confirmForm(
195
- WalletCreationService walletCreationService, String language) {
154
+ void _confirmForm() {
155
if (!_formKey.currentState.validate()) {
156
return;
157
}
158
200
- WalletCredentials credentials;
201
-
202
- if (walletCreationService.type == WalletType.monero) {
203
- credentials = MoneroNewWalletCredentials(
204
- name: nameController.text, language: language);
205
- }
206
-
207
- walletCreationService.create(credentials);
159
+ _walletNewVM.create(
160
+ options: _walletNewVM.hasLanguageSelector
161
+ ? _languageSelectorKey.currentState.selected
162
+ : null);
163
}
164
}
lib/src/screens/new_wallet/new_wallet_type_page.dart
+53
-78
@@ -1,3 +1,4 @@
1
+import 'package:cake_wallet/src/domain/common/wallet_type.dart';
2
import 'package:flutter/material.dart';
3
import 'package:flutter/cupertino.dart';
4
import 'package:cake_wallet/generated/i18n.dart';
@@ -8,14 +9,23 @@ import 'package:cake_wallet/src/screens/new_wallet/widgets/select_button.dart';
9
import 'package:cake_wallet/routes.dart';
10
11
class NewWalletTypePage extends BasePage {
12
+ NewWalletTypePage({this.onTypeSelected});
13
+
14
+ final void Function(BuildContext, WalletType) onTypeSelected;
15
+
16
@override
17
String get title => S.current.new_wallet;
18
19
@override
15
- Widget body(BuildContext context) => WalletTypeForm();
20
+ Widget body(BuildContext context) =>
21
+ WalletTypeForm(onTypeSelected: onTypeSelected);
22
}
23
24
class WalletTypeForm extends StatefulWidget {
25
+ WalletTypeForm({this.onTypeSelected});
26
+
27
+ final void Function(BuildContext, WalletType) onTypeSelected;
28
+
29
@override
30
WalletTypeFormState createState() => WalletTypeFormState();
31
}
@@ -23,35 +33,19 @@ class WalletTypeForm extends StatefulWidget {
33
class WalletTypeFormState extends State<WalletTypeForm> {
34
static const aspectRatioImage = 1.22;
35
26
- final moneroIcon = Image.asset('assets/images/monero.png', height: 24, width: 24);
27
- final bitcoinIcon = Image.asset('assets/images/bitcoin.png', height: 24, width: 24);
36
+ final moneroIcon =
37
+ Image.asset('assets/images/monero.png', height: 24, width: 24);
38
+ final bitcoinIcon =
39
+ Image.asset('assets/images/bitcoin.png', height: 24, width: 24);
40
final walletTypeImage = Image.asset('assets/images/wallet_type.png');
41
30
- bool isDisabledButton;
31
- bool isMoneroSelected;
32
- bool isBitcoinSelected;
33
-
34
- Color moneroBackgroundColor = Colors.transparent;
35
- Color moneroTextColor = Colors.transparent;
36
- Color bitcoinBackgroundColor = Colors.transparent;
37
- Color bitcoinTextColor = Colors.transparent;
42
+ WalletType selected;
43
+ List<WalletType> types;
44
45
@override
46
void initState() {
41
- isDisabledButton = true;
42
- isMoneroSelected = false;
43
- isBitcoinSelected = false;
44
-
47
+ types = [WalletType.bitcoin, WalletType.monero];
48
super.initState();
46
- WidgetsBinding.instance.addPostFrameCallback(afterLayout);
47
- }
48
-
49
- void afterLayout(dynamic _) {
50
- moneroBackgroundColor = Theme.of(context).accentTextTheme.title.backgroundColor;
51
- moneroTextColor = Theme.of(context).primaryTextTheme.title.color;
52
- bitcoinBackgroundColor = Theme.of(context).accentTextTheme.title.backgroundColor;
53
- bitcoinTextColor = Theme.of(context).primaryTextTheme.title.color;
54
- setState(() {});
49
}
50
51
@override
@@ -75,71 +69,52 @@ class WalletTypeFormState extends State<WalletTypeForm> {
69
S.of(context).choose_wallet_currency,
70
textAlign: TextAlign.center,
71
style: TextStyle(
78
- fontSize: 16,
79
- fontWeight: FontWeight.w600,
80
- color: Theme.of(context).primaryTextTheme.title.color
81
- ),
72
+ fontSize: 16,
73
+ fontWeight: FontWeight.w600,
74
+ color: Theme.of(context).primaryTextTheme.title.color),
75
),
76
),
84
- Padding(
85
- padding: EdgeInsets.only(top: 24),
86
- child: SelectButton(
87
- image: bitcoinIcon,
88
- text: 'Bitcoin',
89
- color: bitcoinBackgroundColor,
90
- textColor: bitcoinTextColor,
91
- onTap: () {}),
92
- ),
93
- Padding(
94
- padding: EdgeInsets.only(top: 20),
95
- child: SelectButton(
96
- image: moneroIcon,
97
- text: 'Monero',
98
- color: moneroBackgroundColor,
99
- textColor: moneroTextColor,
100
- onTap: () => onSelectMoneroButton(context)),
101
- )
77
+ ...types.map((type) => Padding(
78
+ padding: EdgeInsets.only(top: 24),
79
+ child: SelectButton(
80
+ image: _iconFor(type),
81
+ text: walletTypeToString(type),
82
+ color: _backgroundColorFor(selected == type),
83
+ textColor: _textColorFor(selected == type),
84
+ onTap: () => setState(() => selected = type)),
85
+ ))
86
],
87
),
104
- bottomSectionPadding: EdgeInsets.only(
105
- left: 24,
106
- right: 24,
107
- bottom: 24
108
- ),
88
+ bottomSectionPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
89
bottomSection: PrimaryButton(
110
- onPressed: () => Navigator.of(context).pushNamed(Routes.newWallet),
90
+ onPressed: () => widget.onTypeSelected(context, selected),
91
text: S.of(context).seed_language_next,
92
color: Colors.green,
93
textColor: Colors.white,
114
- isDisabled: isDisabledButton,
94
+ isDisabled: selected == null,
95
),
96
),
97
);
98
}
99
120
- void onSelectMoneroButton(BuildContext context) {
121
- isMoneroSelected = true;
122
- isBitcoinSelected = false;
123
- isDisabledButton = false;
124
-
125
- moneroBackgroundColor = Theme.of(context).accentTextTheme.title.decorationColor;
126
- moneroTextColor = Theme.of(context).primaryTextTheme.title.backgroundColor;
127
- bitcoinBackgroundColor = Theme.of(context).accentTextTheme.title.backgroundColor;
128
- bitcoinTextColor = Theme.of(context).primaryTextTheme.title.color;
129
-
130
- setState(() {});
100
+ // FIXME: Move color selection inside ui element; add isSelected to buttons.
101
+
102
+ Color _backgroundColorFor(bool isSelected) => isSelected
103
+ ? Theme.of(context).accentTextTheme.title.decorationColor
104
+ : Theme.of(context).accentTextTheme.title.backgroundColor;
105
+
106
+ Color _textColorFor(bool isSelected) => isSelected
107
+ ? Theme.of(context).primaryTextTheme.title.backgroundColor
108
+ : Theme.of(context).primaryTextTheme.title.color;
109
+
110
+ Image _iconFor(WalletType type) {
111
+ switch (type) {
112
+ case WalletType.monero:
113
+ return moneroIcon;
114
+ case WalletType.bitcoin:
115
+ return bitcoinIcon;
116
+ default:
117
+ return null;
118
+ }
119
}
132
-
133
- void onSelectBitcoinButton(BuildContext context) {
134
- isMoneroSelected = false;
135
- isBitcoinSelected = true;
136
- isDisabledButton = false;
137
-
138
- moneroBackgroundColor = Theme.of(context).accentTextTheme.title.backgroundColor;
139
- moneroTextColor = Theme.of(context).primaryTextTheme.title.color;
140
- bitcoinBackgroundColor = moneroBackgroundColor = Theme.of(context).accentTextTheme.title.decorationColor;
141
- bitcoinTextColor = Theme.of(context).primaryTextTheme.title.backgroundColor;
142
-
143
- setState(() {});
144
- }
145
-}
\ No newline at end of file
120
+}
lib/src/screens/receive/receive_page.dart
+232
-321
@@ -1,3 +1,5 @@
1
+import 'package:cake_wallet/palette.dart';
2
+import 'package:cake_wallet/src/screens/base_page.dart';
3
import 'package:flutter/material.dart';
4
import 'package:flutter/cupertino.dart';
5
import 'package:flutter/services.dart';
@@ -16,347 +18,256 @@ import 'package:cake_wallet/src/screens/receive/widgets/header_tile.dart';
18
import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
19
import 'package:cake_wallet/themes.dart';
20
import 'package:cake_wallet/theme_changer.dart';
21
+import 'package:cake_wallet/core/amount_validator.dart';
22
+import 'package:cake_wallet/src/screens/receive/widgets/address_cell.dart';
23
+import 'package:cake_wallet/view_model/address_list/account_list_header.dart';
24
+import 'package:cake_wallet/view_model/address_list/address_list_header.dart';
25
+import 'package:cake_wallet/view_model/address_list/address_list_item.dart';
26
+import 'package:cake_wallet/view_model/address_list/address_list_view_model.dart';
27
20
-class ReceivePage extends StatefulWidget {
21
- @override
22
- ReceivePageState createState() => ReceivePageState();
23
-}
28
+class ReceivePage extends BasePage {
29
+ ReceivePage({this.addressListViewModel})
30
+ : amountController = TextEditingController(),
31
+ _formKey = GlobalKey<FormState>() {
32
+ amountController.addListener(() => addressListViewModel.amount =
33
+ _formKey.currentState.validate() ? amountController.text : '');
34
+ }
35
25
-class ReceivePageState extends State<ReceivePage> {
26
- final amountController = TextEditingController();
27
- final _formKey = GlobalKey<FormState>();
28
- final _backArrowImage = Image.asset('assets/images/back_arrow.png');
29
- final _backArrowImageDarkTheme =
30
- Image.asset('assets/images/back_arrow_dark_theme.png');
36
+ final AddressListViewModel addressListViewModel;
37
+ final TextEditingController amountController;
38
+ final GlobalKey<FormState> _formKey;
39
40
@override
33
- void dispose() {
34
- amountController.dispose();
35
- super.dispose();
36
- }
41
+ Color get backgroundLightColor => Colors.transparent;
42
43
@override
39
- Widget build(BuildContext context) {
40
- final walletStore = Provider.of<WalletStore>(context);
41
- final subaddressListStore = Provider.of<SubaddressListStore>(context);
42
- final accountListStore = Provider.of<AccountListStore>(context);
44
+ Color get backgroundDarkColor => Colors.transparent;
45
44
- final shareImage = Image.asset('assets/images/share.png',
45
- color: Theme.of(context).primaryTextTheme.title.color,
46
- );
47
- final copyImage = Image.asset('assets/images/copy_content.png',
48
- color: Theme.of(context).primaryTextTheme.title.color,
49
- );
46
+ @override
47
+ Widget Function(BuildContext, Widget) get rootWrapper =>
48
+ (BuildContext context, Widget scaffold) => Container(
49
+ decoration: BoxDecoration(
50
+ gradient: LinearGradient(colors: [
51
+ Theme.of(context).scaffoldBackgroundColor,
52
+ Theme.of(context).primaryColor
53
+ ], begin: Alignment.topLeft, end: Alignment.bottomRight)),
54
+ child: scaffold);
55
51
- final currentColor = Theme.of(context).accentTextTheme.subtitle.decorationColor;
52
- final notCurrentColor = Theme.of(context).backgroundColor;
56
+ @override
57
+ Widget middle(BuildContext context) => Text(
58
+ S.of(context).receive,
59
+ style: TextStyle(
60
+ fontSize: 18.0,
61
+ fontWeight: FontWeight.bold,
62
+ color: Theme.of(context).primaryTextTheme.title.color),
63
+ );
64
54
- final currentTextColor = Colors.blue;
55
- final notCurrentTextColor = Theme.of(context).primaryTextTheme.caption.color;
65
+ @override
66
+ Widget trailing(BuildContext context) {
67
+ final shareImage = Image.asset('assets/images/share.png',
68
+ color: Theme.of(context).primaryTextTheme.title.color);
69
57
- final _themeChanger = Provider.of<ThemeChanger>(context);
58
- Image _backButton;
70
+ return SizedBox(
71
+ height: 20.0,
72
+ width: 14.0,
73
+ child: ButtonTheme(
74
+ minWidth: double.minPositive,
75
+ child: FlatButton(
76
+ highlightColor: Colors.transparent,
77
+ splashColor: Colors.transparent,
78
+ padding: EdgeInsets.all(0),
79
+ onPressed: () => Share.text(S.current.share_address,
80
+ addressListViewModel.address.address, 'text/plain'),
81
+ child: shareImage),
82
+ ),
83
+ );
84
+ }
85
60
- if (_themeChanger.getTheme() == Themes.darkTheme) {
61
- _backButton = _backArrowImageDarkTheme;
62
- } else {
63
- _backButton = _backArrowImage;
64
- }
86
+ @override
87
+ Widget build(BuildContext context) {
88
+ return super.build(context);
89
+ }
90
66
- amountController.addListener(() {
67
- if (_formKey.currentState.validate()) {
68
- walletStore.onChangedAmountValue(amountController.text);
69
- } else {
70
- walletStore.onChangedAmountValue('');
71
- }
72
- });
91
+ @override
92
+ Widget body(BuildContext context) {
93
+ final copyImage = Image.asset('assets/images/copy_content.png',
94
+ color: Theme.of(context).primaryTextTheme.title.color);
95
74
- return Scaffold(
75
- resizeToAvoidBottomPadding: false,
76
- body: Container(
77
- height: MediaQuery.of(context).size.height,
78
- width: MediaQuery.of(context).size.width,
79
- padding: EdgeInsets.only(top: 24),
80
- decoration: BoxDecoration(
81
- gradient: LinearGradient(
82
- colors: [
83
- Theme.of(context).scaffoldBackgroundColor,
84
- Theme.of(context).primaryColor
85
- ],
86
- begin: Alignment.centerLeft,
87
- end: Alignment.centerRight
88
- )
96
+ return SingleChildScrollView(
97
+ child: Column(
98
+ children: <Widget>[
99
+ SizedBox(height: 25),
100
+ Row(children: <Widget>[
101
+ Spacer(flex: 4),
102
+ Observer(
103
+ builder: (_) => Flexible(
104
+ flex: 6,
105
+ child: Center(
106
+ child: AspectRatio(
107
+ aspectRatio: 1.0,
108
+ child: QrImage(
109
+ data: addressListViewModel.uri.toString(),
110
+ backgroundColor: Colors.transparent,
111
+ foregroundColor: Theme.of(context)
112
+ .primaryTextTheme
113
+ .display4
114
+ .color,
115
+ ))))),
116
+ Spacer(flex: 4)
117
+ ]),
118
+ Padding(
119
+ padding: EdgeInsets.fromLTRB(24, 40, 24, 0),
120
+ child: Row(
121
+ children: <Widget>[
122
+ Expanded(
123
+ child: Form(
124
+ key: _formKey,
125
+ child: BaseTextFormField(
126
+ controller: amountController,
127
+ keyboardType:
128
+ TextInputType.numberWithOptions(decimal: true),
129
+ inputFormatters: [
130
+ BlacklistingTextInputFormatter(
131
+ RegExp('[\\-|\\ |\\,]'))
132
+ ],
133
+ textAlign: TextAlign.center,
134
+ hintText: S.of(context).receive_amount,
135
+ borderColor: Theme.of(context)
136
+ .primaryTextTheme
137
+ .headline5
138
+ .color
139
+ .withOpacity(0.4),
140
+ validator: AmountValidator(),
141
+ autovalidate: true,
142
+ placeholderTextStyle: TextStyle(
143
+ color: Theme.of(context)
144
+ .primaryTextTheme
145
+ .headline5
146
+ .color,
147
+ fontSize: 20,
148
+ fontWeight: FontWeight.w600))))
149
+ ],
150
+ ),
151
),
90
- child: Column(
91
- children: <Widget>[
92
- Container(
93
- padding: EdgeInsets.only(
94
- top: 10,
95
- bottom: 20,
96
- left: 5,
97
- right: 10
98
- ),
99
- child: Row(
100
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
101
- crossAxisAlignment: CrossAxisAlignment.center,
102
- children: <Widget>[
103
- SizedBox(
104
- height: 44,
105
- width: 44,
106
- child: ButtonTheme(
107
- minWidth: double.minPositive,
108
- child: FlatButton(
109
- highlightColor: Colors.transparent,
110
- splashColor: Colors.transparent,
111
- padding: EdgeInsets.all(0),
112
- onPressed: () => Navigator.of(context).pop(),
113
- child: _backButton),
114
- ),
115
- ),
116
- Text(
117
- S.of(context).receive,
118
- style: TextStyle(
119
- fontSize: 18.0,
120
- fontWeight: FontWeight.bold,
121
- color: Theme.of(context).primaryTextTheme.title.color),
122
- ),
123
- SizedBox(
124
- height: 44.0,
125
- width: 44.0,
126
- child: ButtonTheme(
127
- minWidth: double.minPositive,
128
- child: FlatButton(
129
- highlightColor: Colors.transparent,
130
- splashColor: Colors.transparent,
131
- padding: EdgeInsets.all(0),
132
- onPressed: () => Share.text(
133
- S.current.share_address, walletStore.subaddress.address, 'text/plain'),
134
- child: shareImage),
135
- ),
136
- )
137
- ],
138
- ),
139
- ),
140
- Expanded(
141
- child: SingleChildScrollView(
142
- child: Column(
143
- children: <Widget>[
144
- Observer(builder: (_) {
145
- return Row(
146
- children: <Widget>[
147
- Spacer(
148
- flex: 1,
149
- ),
150
- Flexible(
151
- flex: 2,
152
- child: Center(
153
- child: AspectRatio(
154
- aspectRatio: 1.0,
155
- child: QrImage(
156
- data: walletStore.subaddress.address +
157
- walletStore.amountValue,
158
- backgroundColor: Colors.transparent,
159
- foregroundColor: Theme.of(context).primaryTextTheme.display4.color,
160
- ),
161
- ),
162
- )),
163
- Spacer(
164
- flex: 1,
165
- )
166
- ],
167
- );
168
- }),
169
- Padding(
170
- padding: EdgeInsets.all(24),
171
- child: Row(
172
- children: <Widget>[
173
- Expanded(
174
- child: Form(
175
- key: _formKey,
176
- child: BaseTextFormField(
177
- controller: amountController,
178
- keyboardType: TextInputType.numberWithOptions(decimal: true),
179
- inputFormatters: [
180
- BlacklistingTextInputFormatter(
181
- RegExp('[\\-|\\ |\\,]'))
182
- ],
183
- textAlign: TextAlign.center,
184
- hintText: S.of(context).receive_amount,
185
- borderColor: Theme.of(context).primaryTextTheme.caption.color,
186
- validator: (value) {
187
- walletStore.validateAmount(value);
188
- return walletStore.errorMessage;
189
- },
190
- autovalidate: true,
191
- )
192
- )
193
- )
194
- ],
195
- ),
196
- ),
197
- Padding(
198
- padding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
199
- child: Builder(
200
- builder: (context) => Observer(
201
- builder: (context) => GestureDetector(
202
- onTap: () {
203
- Clipboard.setData(ClipboardData(
204
- text: walletStore.subaddress.address));
205
- Scaffold.of(context).showSnackBar(SnackBar(
206
- content: Text(
207
- S.of(context).copied_to_clipboard,
208
- style: TextStyle(color: Colors.white),
209
- ),
210
- backgroundColor: Colors.green,
211
- duration: Duration(milliseconds: 500),
212
- ));
213
- },
214
- child: Container(
215
- height: 48,
216
- padding: EdgeInsets.only(left: 24, right: 24),
217
- decoration: BoxDecoration(
218
- borderRadius: BorderRadius.all(Radius.circular(24)),
219
- color: Theme.of(context).primaryTextTheme.overline.color
220
- ),
221
- child: Row(
222
- mainAxisSize: MainAxisSize.max,
223
- children: <Widget>[
224
- Expanded(
225
- child: Text(
226
- walletStore.subaddress.address,
227
- maxLines: 1,
228
- overflow: TextOverflow.ellipsis,
229
- style: TextStyle(
230
- fontSize: 14,
231
- fontWeight: FontWeight.w600,
232
- color: Theme.of(context).primaryTextTheme.title.color
233
- ),
234
- ),
235
- ),
236
- Padding(
237
- padding: EdgeInsets.only(left: 12),
238
- child: copyImage,
239
- )
240
- ],
152
+ Padding(
153
+ padding: EdgeInsets.only(left: 24, right: 24, bottom: 40, top: 40),
154
+ child: Builder(
155
+ builder: (context) => Observer(
156
+ builder: (context) => GestureDetector(
157
+ onTap: () {
158
+ Clipboard.setData(ClipboardData(
159
+ text: addressListViewModel.address.address));
160
+ Scaffold.of(context).showSnackBar(SnackBar(
161
+ content: Text(
162
+ S.of(context).copied_to_clipboard,
163
+ style: TextStyle(color: Colors.white),
164
+ ),
165
+ backgroundColor: Colors.green,
166
+ duration: Duration(milliseconds: 500),
167
+ ));
168
+ },
169
+ child: Container(
170
+ height: 48,
171
+ padding: EdgeInsets.only(left: 24, right: 24),
172
+ decoration: BoxDecoration(
173
+ borderRadius:
174
+ BorderRadius.all(Radius.circular(24)),
175
+ color: Theme.of(context)
176
+ .primaryTextTheme
177
+ .overline
178
+ .color),
179
+ child: Row(
180
+ mainAxisSize: MainAxisSize.max,
181
+ children: <Widget>[
182
+ Expanded(
183
+ child: Text(
184
+ addressListViewModel.address.address,
185
+ maxLines: 1,
186
+ overflow: TextOverflow.ellipsis,
187
+ style: TextStyle(
188
+ fontSize: 18,
189
+ fontWeight: FontWeight.w600,
190
+ color: Theme.of(context)
191
+ .primaryTextTheme
192
+ .title
193
+ .color),
194
),
195
),
243
- )
244
- )
245
- ),
246
- ),
247
- Observer(
248
- builder: (_) => ListView.separated(
249
- separatorBuilder: (context, index) => Divider(
250
- height: 1,
251
- color: Theme.of(context).dividerColor,
252
- ),
253
- shrinkWrap: true,
254
- physics: NeverScrollableScrollPhysics(),
255
- itemCount: subaddressListStore.subaddresses.length + 2,
256
- itemBuilder: (context, index) {
257
-
258
- if (index == 0) {
259
- return ClipRRect(
260
- borderRadius: BorderRadius.only(
261
- topLeft: Radius.circular(24),
262
- topRight: Radius.circular(24)
263
- ),
264
- child: HeaderTile(
265
- onTap: () async {
266
- await showDialog<void>(
267
- context: context,
268
- builder: (BuildContext context) {
269
- return AccountListPage(accountListStore: accountListStore);
270
- }
271
- );
272
- },
273
- title: walletStore.account.label,
274
- icon: Icon(
275
- Icons.arrow_forward_ios,
276
- size: 14,
277
- color: Theme.of(context).primaryTextTheme.title.color,
278
- )
279
- ),
280
- );
281
- }
282
-
283
- if (index == 1) {
284
- return HeaderTile(
285
- onTap: () => Navigator.of(context)
286
- .pushNamed(Routes.newSubaddress),
287
- title: S.of(context).subaddresses,
288
- icon: Icon(
289
- Icons.add,
290
- size: 20,
291
- color: Theme.of(context).primaryTextTheme.title.color,
292
- )
293
- );
294
- }
295
-
296
- index -= 2;
196
+ Padding(
197
+ padding: EdgeInsets.only(left: 12),
198
+ child: copyImage,
199
+ )
200
+ ],
201
+ ),
202
+ ),
203
+ ))),
204
+ ),
205
+ Observer(
206
+ builder: (_) => ListView.separated(
207
+ separatorBuilder: (context, _) =>
208
+ Divider(height: 1, color: Theme.of(context).dividerColor),
209
+ shrinkWrap: true,
210
+ physics: NeverScrollableScrollPhysics(),
211
+ itemCount: addressListViewModel.items.length,
212
+ itemBuilder: (context, index) {
213
+ final item = addressListViewModel.items[index];
214
+ Widget cell = Container();
215
298
- return Observer(
299
- builder: (_) {
300
- final subaddress = subaddressListStore.subaddresses[index];
301
- final isCurrent =
302
- walletStore.subaddress.address == subaddress.address;
216
+ if (item is AccountListHeader) {
217
+ cell = HeaderTile(
218
+ onTap: () async {
219
+ await showDialog<void>(
220
+ context: context,
221
+ builder: (BuildContext context) {
222
+// return AccountListPage(
223
+// accountListStore:
224
+// accountListStore);
225
+ });
226
+ },
227
+ title: addressListViewModel.accountLabel,
228
+ icon: Icon(
229
+ Icons.arrow_forward_ios,
230
+ size: 14,
231
+ color:
232
+ Theme.of(context).primaryTextTheme.title.color,
233
+ ));
234
+ }
235
304
- final label = subaddress.label.isNotEmpty
305
- ? subaddress.label
306
- : subaddress.address;
236
+ if (item is AddressListHeader) {
237
+ cell = HeaderTile(
238
+ onTap: () => Navigator.of(context)
239
+ .pushNamed(Routes.newSubaddress),
240
+ title: S.of(context).subaddresses,
241
+ icon: Icon(
242
+ Icons.add,
243
+ size: 20,
244
+ color:
245
+ Theme.of(context).primaryTextTheme.title.color,
246
+ ));
247
+ }
248
308
- final content = InkWell(
309
- onTap: () => walletStore.setSubaddress(subaddress),
310
- child: Container(
311
- color: isCurrent ? currentColor : notCurrentColor,
312
- padding: EdgeInsets.only(
313
- left: 24,
314
- right: 24,
315
- top: 28,
316
- bottom: 28
317
- ),
318
- child: Text(
319
- label,
320
- style: TextStyle(
321
- fontSize: subaddress.label.isNotEmpty
322
- ? 18 : 10,
323
- fontWeight: FontWeight.bold,
324
- color: isCurrent
325
- ? currentTextColor
326
- : notCurrentTextColor,
327
- ),
328
- ),
329
- ),
330
- );
249
+ if (item is AddressListItem) {
250
+ cell = Observer(
251
+ builder: (_) => AddressCell.fromItem(item,
252
+ isCurrent: item.address ==
253
+ addressListViewModel.address.address,
254
+ onTap: (_) =>
255
+ addressListViewModel.address = item,
256
+ onEdit: () => Navigator.of(context)
257
+ .pushNamed(Routes.newSubaddress, arguments: item)));
258
+ }
259
332
- return isCurrent
333
- ? content
334
- : Slidable(
335
- key: Key(subaddress.address),
336
- actionPane: SlidableDrawerActionPane(),
337
- child: content,
338
- secondaryActions: <Widget>[
339
- IconSlideAction(
340
- caption: S.of(context).edit,
341
- color: Theme.of(context).primaryTextTheme.overline.color,
342
- icon: Icons.edit,
343
- onTap: () => Navigator.of(context)
344
- .pushNamed(Routes.newSubaddress, arguments: subaddress),
345
- )
346
- ]
347
- );
348
- }
349
- );
350
- }
351
- )
352
- ),
353
- ],
354
- ),
355
- )
356
- )
357
- ],
358
- )
260
+ return index != 0
261
+ ? cell
262
+ : ClipRRect(
263
+ borderRadius: BorderRadius.only(
264
+ topLeft: Radius.circular(24),
265
+ topRight: Radius.circular(24)),
266
+ child: cell,
267
+ );
268
+ })),
269
+ ],
270
),
271
);
272
}
362
-}
\ No newline at end of file
273
+}
lib/src/screens/receive/widgets/address_cell.dart
new
+70
@@ -0,0 +1,70 @@
1
+import 'package:flutter/material.dart';
2
+import 'package:flutter_slidable/flutter_slidable.dart';
3
+import 'package:cake_wallet/generated/i18n.dart';
4
+import 'package:cake_wallet/view_model/address_list/address_list_item.dart';
5
+
6
+class AddressCell extends StatelessWidget {
7
+ factory AddressCell.fromItem(AddressListItem item,
8
+ {@required bool isCurrent,
9
+ Function(String) onTap,
10
+ Function() onEdit}) =>
11
+ AddressCell(
12
+ address: item.address,
13
+ name: item.name,
14
+ isCurrent: isCurrent,
15
+ onTap: onTap,
16
+ onEdit: onEdit);
17
+
18
+ AddressCell(
19
+ {@required this.address,
20
+ @required this.name,
21
+ @required this.isCurrent,
22
+ this.onTap,
23
+ this.onEdit});
24
+
25
+ final String address;
26
+ final String name;
27
+ final bool isCurrent;
28
+ final Function(String) onTap;
29
+ final Function() onEdit;
30
+
31
+ String get label => name ?? address;
32
+
33
+ @override
34
+ Widget build(BuildContext context) {
35
+ const currentTextColor = Colors.blue; // FIXME: Why it's defined here ?
36
+ final currentColor =
37
+ Theme.of(context).accentTextTheme.subtitle.decorationColor;
38
+ final notCurrentColor = Theme.of(context).backgroundColor;
39
+ final notCurrentTextColor =
40
+ Theme.of(context).primaryTextTheme.caption.color;
41
+ final Widget cell = InkWell(
42
+ onTap: () => onTap(address),
43
+ child: Container(
44
+ color: isCurrent ? currentColor : notCurrentColor,
45
+ padding: EdgeInsets.only(left: 24, right: 24, top: 28, bottom: 28),
46
+ child: Text(
47
+ name ?? address,
48
+ style: TextStyle(
49
+ fontSize: name?.isNotEmpty ?? false ? 18 : 10,
50
+ fontWeight: FontWeight.bold,
51
+ color: isCurrent ? currentTextColor : notCurrentTextColor,
52
+ ),
53
+ ),
54
+ ));
55
+
56
+ return isCurrent
57
+ ? cell
58
+ : Slidable(
59
+ key: Key(address),
60
+ actionPane: SlidableDrawerActionPane(),
61
+ child: cell,
62
+ secondaryActions: <Widget>[
63
+ IconSlideAction(
64
+ caption: S.of(context).edit,
65
+ color: Theme.of(context).primaryTextTheme.overline.color,
66
+ icon: Icons.edit,
67
+ onTap: () => onEdit?.call())
68
+ ]);
69
+ }
70
+}
lib/src/screens/restore/restore_wallet_from_seed_details.dart
+78
-69
@@ -1,26 +1,35 @@
1
import 'package:mobx/mobx.dart';
2
-import 'package:provider/provider.dart';
2
import 'package:flutter/material.dart';
4
-import 'package:flutter/cupertino.dart';
3
import 'package:flutter_mobx/flutter_mobx.dart';
4
import 'package:cake_wallet/generated/i18n.dart';
7
-import 'package:cake_wallet/src/stores/wallet_restoration/wallet_restoration_store.dart';
8
-import 'package:cake_wallet/src/stores/wallet_restoration/wallet_restoration_state.dart';
5
+import 'package:cake_wallet/core/validator.dart';
6
+import 'package:cake_wallet/view_model/wallet_creation_state.dart';
7
import 'package:cake_wallet/src/screens/base_page.dart';
8
import 'package:cake_wallet/src/widgets/blockchain_height_widget.dart';
9
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
10
import 'package:cake_wallet/src/widgets/primary_button.dart';
11
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
12
+import 'package:cake_wallet/view_model/wallet_restoration_from_seed_vm.dart';
13
14
class RestoreWalletFromSeedDetailsPage extends BasePage {
15
+ RestoreWalletFromSeedDetailsPage(
16
+ {@required this.walletRestorationFromSeedVM});
17
+
18
+ final WalletRestorationFromSeedVM walletRestorationFromSeedVM;
19
+
20
@override
21
String get title => S.current.restore_wallet_restore_description;
22
23
@override
20
- Widget body(BuildContext context) => RestoreFromSeedDetailsForm();
24
+ Widget body(BuildContext context) => RestoreFromSeedDetailsForm(
25
+ walletRestorationFromSeedVM: walletRestorationFromSeedVM);
26
}
27
28
class RestoreFromSeedDetailsForm extends StatefulWidget {
29
+ RestoreFromSeedDetailsForm({@required this.walletRestorationFromSeedVM});
30
+
31
+ final WalletRestorationFromSeedVM walletRestorationFromSeedVM;
32
+
33
@override
34
_RestoreFromSeedDetailsFormState createState() =>
35
_RestoreFromSeedDetailsFormState();
@@ -31,31 +40,17 @@ class _RestoreFromSeedDetailsFormState
40
final _formKey = GlobalKey<FormState>();
41
final _blockchainHeightKey = GlobalKey<BlockchainHeightState>();
42
final _nameController = TextEditingController();
43
+ ReactionDisposer _stateReaction;
44
45
@override
36
- void dispose() {
37
- _nameController.dispose();
38
- super.dispose();
39
- }
40
-
41
- @override
42
- Widget build(BuildContext context) {
43
- final walletRestorationStore = Provider.of<WalletRestorationStore>(context);
44
-
45
- _nameController.addListener(() {
46
- if (_nameController.text.isNotEmpty) {
47
- walletRestorationStore.setDisabledState(false);
48
- } else {
49
- walletRestorationStore.setDisabledState(true);
50
- }
51
- });
52
-
53
- reaction((_) => walletRestorationStore.state, (WalletRestorationState state) {
54
- if (state is WalletRestoredSuccessfully) {
46
+ void initState() {
47
+ _stateReaction = reaction((_) => widget.walletRestorationFromSeedVM.state,
48
+ (WalletCreationState state) {
49
+ if (state is WalletCreatedSuccessfully) {
50
Navigator.of(context).popUntil((route) => route.isFirst);
51
}
52
58
- if (state is WalletRestorationFailure) {
53
+ if (state is WalletCreationFailure) {
54
WidgetsBinding.instance.addPostFrameCallback((_) {
55
showDialog<void>(
56
context: context,
@@ -64,73 +59,87 @@ class _RestoreFromSeedDetailsFormState
59
alertTitle: S.current.restore_title_from_seed,
60
alertContent: state.error,
61
buttonText: S.of(context).ok,
67
- buttonAction: () => Navigator.of(context).pop()
68
- );
62
+ buttonAction: () => Navigator.of(context).pop());
63
});
64
});
65
}
66
});
67
68
+ _nameController.addListener(
69
+ () => widget.walletRestorationFromSeedVM.name = _nameController.text);
70
+ super.initState();
71
+ }
72
+
73
+ @override
74
+ void dispose() {
75
+ _nameController.dispose();
76
+ _stateReaction.reaction.dispose();
77
+ super.dispose();
78
+ }
79
+
80
+ @override
81
+ Widget build(BuildContext context) {
82
return Container(
83
padding: EdgeInsets.only(left: 24, right: 24),
84
child: ScrollableWithBottomSection(
85
contentPadding: EdgeInsets.only(bottom: 24.0),
86
content: Form(
87
key: _formKey,
80
- child: Column(
88
+ child: Column(children: <Widget>[
89
+ Row(
90
children: <Widget>[
82
- Row(
83
- children: <Widget>[
84
- Flexible(
85
- child: Container(
86
- padding: EdgeInsets.only(top: 20.0),
87
- child: TextFormField(
88
- style: TextStyle(
89
- fontSize: 16.0,
90
- color: Theme.of(context).primaryTextTheme.title.color
91
- ),
92
- controller: _nameController,
93
- decoration: InputDecoration(
94
- hintStyle: TextStyle(
95
- color: Theme.of(context).primaryTextTheme.caption.color,
96
- fontSize: 16
97
- ),
98
- hintText: S.of(context).restore_wallet_name,
99
- focusedBorder: UnderlineInputBorder(
100
- borderSide: BorderSide(
101
- color: Theme.of(context).dividerColor,
102
- width: 1.0)),
103
- enabledBorder: UnderlineInputBorder(
104
- borderSide: BorderSide(
105
- color: Theme.of(context).dividerColor,
106
- width: 1.0))),
107
- validator: (value) {
108
- walletRestorationStore
109
- .validateWalletName(value);
110
- return walletRestorationStore.errorMessage;
111
- },
112
- ),
113
- ))
114
- ],
115
- ),
116
- BlockchainHeightWidget(key: _blockchainHeightKey),
117
- ]),
91
+ Flexible(
92
+ child: Container(
93
+ padding: EdgeInsets.only(top: 20.0),
94
+ child: TextFormField(
95
+ style: TextStyle(
96
+ fontSize: 16.0,
97
+ color: Theme.of(context).primaryTextTheme.title.color),
98
+ controller: _nameController,
99
+ decoration: InputDecoration(
100
+ hintStyle: TextStyle(
101
+ color: Theme.of(context)
102
+ .primaryTextTheme
103
+ .caption
104
+ .color,
105
+ fontSize: 16),
106
+ hintText: S.of(context).restore_wallet_name,
107
+ focusedBorder: UnderlineInputBorder(
108
+ borderSide: BorderSide(
109
+ color: Theme.of(context).dividerColor,
110
+ width: 1.0)),
111
+ enabledBorder: UnderlineInputBorder(
112
+ borderSide: BorderSide(
113
+ color: Theme.of(context).dividerColor,
114
+ width: 1.0))),
115
+ validator: WalletNameValidator(),
116
+ ),
117
+ ))
118
+ ],
119
+ ),
120
+ if (widget.walletRestorationFromSeedVM.hasRestorationHeight)
121
+ BlockchainHeightWidget(
122
+ key: _blockchainHeightKey,
123
+ onHeightChange: (height) {
124
+ widget.walletRestorationFromSeedVM.height = height;
125
+ print(height);
126
+ }),
127
+ ]),
128
),
129
bottomSectionPadding: EdgeInsets.only(bottom: 24),
130
bottomSection: Observer(builder: (_) {
131
return LoadingPrimaryButton(
132
onPressed: () {
133
if (_formKey.currentState.validate()) {
124
- walletRestorationStore.restoreFromSeed(
125
- name: _nameController.text,
126
- restoreHeight: _blockchainHeightKey.currentState.height);
134
+ widget.walletRestorationFromSeedVM.create();
135
}
136
},
129
- isLoading: walletRestorationStore.state is WalletIsRestoring,
137
+ isLoading:
138
+ widget.walletRestorationFromSeedVM.state is WalletCreating,
139
text: S.of(context).restore_recover,
140
color: Colors.green,
141
textColor: Colors.white,
133
- isDisabled: walletRestorationStore.disabledState,
142
+ isDisabled: _nameController.text.isNotEmpty,
143
);
144
}),
145
),
lib/src/screens/restore/restore_wallet_from_seed_page.dart
+18
-23
@@ -1,27 +1,19 @@
1
-import 'package:provider/provider.dart';
1
import 'package:flutter/material.dart';
3
-import 'package:flutter/cupertino.dart';
2
import 'package:flutter/services.dart';
5
-import 'package:shared_preferences/shared_preferences.dart';
3
import 'package:cake_wallet/routes.dart';
4
import 'package:cake_wallet/generated/i18n.dart';
8
-import 'package:cake_wallet/src/domain/services/wallet_list_service.dart';
9
-import 'package:cake_wallet/src/domain/services/wallet_service.dart';
5
import 'package:cake_wallet/src/screens/base_page.dart';
11
-import 'package:cake_wallet/src/stores/wallet_restoration/wallet_restoration_store.dart';
6
import 'package:cake_wallet/src/widgets/seed_widget.dart';
13
-import 'package:cake_wallet/src/stores/seed_language/seed_language_store.dart';
7
+import 'package:cake_wallet/src/domain/common/wallet_type.dart';
8
+import 'package:cake_wallet/core/seed_validator.dart';
9
import 'package:cake_wallet/palette.dart';
10
+import 'package:cake_wallet/core/mnemonic_length.dart';
11
12
class RestoreWalletFromSeedPage extends BasePage {
17
- RestoreWalletFromSeedPage(
18
- {@required this.walletsService,
19
- @required this.walletService,
20
- @required this.sharedPreferences});
13
+ RestoreWalletFromSeedPage({@required this.type, @required this.language});
14
22
- final WalletListService walletsService;
23
- final WalletService walletService;
24
- final SharedPreferences sharedPreferences;
15
+ final WalletType type;
16
+ final String language;
17
final formKey = GlobalKey<_RestoreFromSeedFormState>();
18
19
@override
@@ -34,11 +26,14 @@ class RestoreWalletFromSeedPage extends BasePage {
26
Color get backgroundDarkColor => PaletteDark.lightNightBlue;
27
28
@override
37
- Widget body(BuildContext context) => RestoreFromSeedForm(key: formKey);
29
+ Widget body(BuildContext context) =>
30
+ RestoreFromSeedForm(key: formKey, type: type, language: language);
31
}
32
33
class RestoreFromSeedForm extends StatefulWidget {
41
- RestoreFromSeedForm({Key key}) : super(key: key);
34
+ RestoreFromSeedForm({Key key, this.type, this.language}) : super(key: key);
35
+ final WalletType type;
36
+ final String language;
37
38
@override
39
_RestoreFromSeedFormState createState() => _RestoreFromSeedFormState();
@@ -46,13 +41,11 @@ class RestoreFromSeedForm extends StatefulWidget {
41
42
class _RestoreFromSeedFormState extends State<RestoreFromSeedForm> {
43
final _seedKey = GlobalKey<SeedWidgetState>();
49
- void clear() => _seedKey.currentState.clear();
44
+
45
+ String mnemonic() => _seedKey.currentState.items.map((e) => e.text).join(' ');
46
47
@override
48
Widget build(BuildContext context) {
53
- final walletRestorationStore = Provider.of<WalletRestorationStore>(context);
54
- final seedLanguageStore = Provider.of<SeedLanguageStore>(context);
55
-
49
return GestureDetector(
50
onTap: () =>
51
SystemChannels.textInput.invokeMethod<void>('TextInput.hide'),
@@ -60,11 +53,13 @@ class _RestoreFromSeedFormState extends State<RestoreFromSeedForm> {
53
color: Theme.of(context).backgroundColor,
54
child: SeedWidget(
55
key: _seedKey,
63
- onMnemoticChange: (seed) => walletRestorationStore.setSeed(seed),
56
+ maxLength: mnemonicLength(widget.type),
57
+ onMnemonicChange: (seed) => null,
58
onFinish: () => Navigator.of(context).pushNamed(
59
Routes.restoreWalletFromSeedDetails,
66
- arguments: _seedKey.currentState.items),
67
- seedLanguage: seedLanguageStore.selectedSeedLanguage,
60
+ arguments: [widget.type, widget.language, mnemonic()]),
61
+ validator:
62
+ SeedValidator(type: widget.type, language: widget.language),
63
),
64
),
65
);
lib/src/screens/restore/restore_wallet_options_page.dart
+48
-20
@@ -1,19 +1,21 @@
1
import 'package:flutter/material.dart';
2
-import 'package:flutter/cupertino.dart';
2
+import 'package:provider/provider.dart';
3
import 'package:cake_wallet/routes.dart';
4
-import 'package:cake_wallet/palette.dart';
4
import 'package:cake_wallet/src/screens/restore/widgets/restore_button.dart';
5
import 'package:cake_wallet/src/screens/base_page.dart';
6
import 'package:cake_wallet/src/domain/common/wallet_type.dart';
7
import 'package:cake_wallet/generated/i18n.dart';
8
import 'package:cake_wallet/src/stores/seed_language/seed_language_store.dart';
10
-import 'package:provider/provider.dart';
9
10
class RestoreWalletOptionsPage extends BasePage {
13
- RestoreWalletOptionsPage({@required this.type});
11
+ RestoreWalletOptionsPage(
12
+ {@required this.type,
13
+ @required this.onRestoreFromSeed,
14
+ @required this.onRestoreFromKeys});
15
15
- static const _aspectRatioImage = 2.086;
16
final WalletType type;
17
+ final Function(BuildContext context) onRestoreFromSeed;
18
+ final Function(BuildContext context) onRestoreFromKeys;
19
20
@override
21
String get title => S.current.restore_seed_keys_restore;
@@ -23,8 +25,6 @@ class RestoreWalletOptionsPage extends BasePage {
25
26
@override
27
Widget body(BuildContext context) {
26
- final seedLanguageStore = Provider.of<SeedLanguageStore>(context);
27
-
28
return Container(
29
width: double.infinity,
30
height: double.infinity,
@@ -33,28 +33,56 @@ class RestoreWalletOptionsPage extends BasePage {
33
child: Column(
34
children: <Widget>[
35
RestoreButton(
36
- onPressed: () {
37
- seedLanguageStore
38
- .setCurrentRoute(Routes.restoreWalletFromSeed);
39
- Navigator.pushNamed(context, Routes.seedLanguage);
40
- },
36
+ onPressed: () => onRestoreFromSeed(context),
37
image: imageSeed,
38
title: S.of(context).restore_title_from_seed,
43
- description: S.of(context).restore_description_from_seed),
39
+ description: _fromSeedDescription(context)),
40
Padding(
41
padding: EdgeInsets.only(top: 24),
42
child: RestoreButton(
47
- onPressed: () {
48
- seedLanguageStore
49
- .setCurrentRoute(Routes.restoreWalletFromKeys);
50
- Navigator.pushNamed(context, Routes.seedLanguage);
51
- },
43
+ onPressed: () => onRestoreFromKeys(context),
44
image: imageKeys,
53
- title: S.of(context).restore_title_from_keys,
54
- description: S.of(context).restore_description_from_keys),
45
+ title: _fromKeyTitle(context),
46
+ description: _fromKeyDescription(context)),
47
)
48
],
49
),
50
));
51
}
52
+
53
+ String _fromSeedDescription(BuildContext context) {
54
+ switch (type) {
55
+ case WalletType.monero:
56
+ return S.of(context).restore_description_from_seed;
57
+ case WalletType.bitcoin:
58
+ // TODO: Add transaction for bitcoin description.
59
+ return 'Restore your wallet from 12 word combination code';
60
+ default:
61
+ return '';
62
+ }
63
+ }
64
+
65
+ String _fromKeyDescription(BuildContext context) {
66
+ switch (type) {
67
+ case WalletType.monero:
68
+ return S.of(context).restore_description_from_keys;
69
+ case WalletType.bitcoin:
70
+ // TODO: Add transaction for bitcoin description.
71
+ return 'Restore your wallet from generated WIF string from your private keys';
72
+ default:
73
+ return '';
74
+ }
75
+ }
76
+
77
+ String _fromKeyTitle(BuildContext context) {
78
+ switch (type) {
79
+ case WalletType.monero:
80
+ return S.of(context).restore_title_from_keys;
81
+ case WalletType.bitcoin:
82
+ // TODO: Add transaction for bitcoin description.
83
+ return 'Restore from WIF';
84
+ default:
85
+ return '';
86
+ }
87
+ }
88
}
lib/src/screens/root/root.dart
+65
-45
@@ -1,10 +1,15 @@
1
+import 'package:cake_wallet/di.dart';
2
+import 'package:cake_wallet/src/screens/dashboard/dashboard_page.dart';
3
+import 'package:cake_wallet/store/app_store.dart';
4
import 'package:flutter/material.dart';
5
import 'package:flutter_mobx/flutter_mobx.dart';
6
import 'package:hive/hive.dart';
7
import 'package:provider/provider.dart';
8
import 'package:shared_preferences/shared_preferences.dart';
9
import 'package:cake_wallet/routes.dart';
7
-import 'package:cake_wallet/src/stores/authentication/authentication_store.dart';
10
+import 'package:cake_wallet/store/authentication_store.dart';
11
+
12
+//import 'package:cake_wallet/src/stores/authentication/authentication_store.dart';
13
import 'package:cake_wallet/src/stores/price/price_store.dart';
14
import 'package:cake_wallet/src/stores/settings/settings_store.dart';
15
import 'package:cake_wallet/src/stores/wallet/wallet_store.dart';
@@ -21,7 +26,10 @@ import 'package:cake_wallet/src/screens/auth/auth_page.dart';
26
import 'package:cake_wallet/src/screens/welcome/create_welcome_page.dart';
27
28
class Root extends StatefulWidget {
24
- Root({Key key}) : super(key: key);
29
+ Root({Key key, this.authenticationStore, this.appStore}) : super(key: key);
30
+
31
+ final AuthenticationStore authenticationStore;
32
+ final AppStore appStore;
33
34
@override
35
RootState createState() => RootState();
@@ -30,7 +38,6 @@ class Root extends StatefulWidget {
38
class RootState extends State<Root> with WidgetsBindingObserver {
39
bool _isInactive;
40
bool _postFrameCallback;
33
- AuthenticationStore _authenticationStore;
41
42
@override
43
void initState() {
@@ -48,12 +55,12 @@ class RootState extends State<Root> with WidgetsBindingObserver {
55
return;
56
}
57
51
- if (!_isInactive &&
52
- _authenticationStore.state ==
53
- AuthenticationState.authenticated ||
54
- _authenticationStore.state == AuthenticationState.active) {
55
- setState(() => _isInactive = true);
56
- }
58
+// if (!_isInactive &&
59
+// widget.authenticationStore.state ==
60
+// AuthenticationState.authenticated ||
61
+// widget.authenticationStore.state == AuthenticationState.active) {
62
+// setState(() => _isInactive = true);
63
+// }
64
65
break;
66
default:
@@ -63,18 +70,18 @@ class RootState extends State<Root> with WidgetsBindingObserver {
70
71
@override
72
Widget build(BuildContext context) {
66
- _authenticationStore = Provider.of<AuthenticationStore>(context);
67
- final sharedPreferences = Provider.of<SharedPreferences>(context);
68
- final walletListService = Provider.of<WalletListService>(context);
69
- final walletService = Provider.of<WalletService>(context);
70
- final userService = Provider.of<UserService>(context);
71
- final priceStore = Provider.of<PriceStore>(context);
72
- final authenticationStore = Provider.of<AuthenticationStore>(context);
73
- final trades = Provider.of<Box<Trade>>(context);
74
- final transactionDescriptions =
75
- Provider.of<Box<TransactionDescription>>(context);
76
- final walletStore = Provider.of<WalletStore>(context);
77
- final settingsStore = Provider.of<SettingsStore>(context);
73
+// _authenticationStore = Provider.of<AuthenticationStore>(context);
74
+// final sharedPreferences = Provider.of<SharedPreferences>(context);
75
+// final walletListService = Provider.of<WalletListService>(context);
76
+// final walletService = Provider.of<WalletService>(context);
77
+// final userService = Provider.of<UserService>(context);
78
+// final priceStore = Provider.of<PriceStore>(context);
79
+// final authenticationStore = Provider.of<AuthenticationStore>(context);
80
+// final trades = Provider.of<Box<Trade>>(context);
81
+// final transactionDescriptions =
82
+// Provider.of<Box<TransactionDescription>>(context);
83
+// final walletStore = Provider.of<WalletStore>(context);
84
+// final settingsStore = Provider.of<SettingsStore>(context);
85
86
if (_isInactive && !_postFrameCallback) {
87
_postFrameCallback = true;
@@ -96,38 +103,51 @@ class RootState extends State<Root> with WidgetsBindingObserver {
103
}
104
105
return Observer(builder: (_) {
99
- final state = _authenticationStore.state;
106
+ final state = widget.authenticationStore.state;
107
+ print(state);
108
if (state == AuthenticationState.denied) {
109
return createWelcomePage();
110
}
111
104
- if (state == AuthenticationState.readyToLogin) {
105
- return createLoginPage(
106
- sharedPreferences: sharedPreferences,
107
- userService: userService,
108
- walletService: walletService,
109
- walletListService: walletListService,
110
- authenticationStore: authenticationStore);
112
+ if (state == AuthenticationState.installed) {
113
+ return getIt.get<AuthPage>();
114
}
115
113
- if (state == AuthenticationState.authenticated ||
114
- state == AuthenticationState.restored) {
115
- return createDashboardPage(
116
- walletService: walletService,
117
- priceStore: priceStore,
118
- trades: trades,
119
- transactionDescriptions: transactionDescriptions,
120
- walletStore: walletStore,
121
- settingsStore: settingsStore);
116
+ if (state == AuthenticationState.allowed) {
117
+ return getIt.get<DashboardPage>();
118
}
119
124
- if (state == AuthenticationState.created) {
125
- return createSeedPage(
126
- settingsStore: settingsStore,
127
- walletService: walletService,
128
- callback: () =>
129
- _authenticationStore.state = AuthenticationState.authenticated);
130
- }
120
+// if (state == AuthenticationState.denied) {
121
+// return createWelcomePage();
122
+// }
123
+
124
+// if (state == AuthenticationState.readyToLogin) {
125
+// return createLoginPage(
126
+// sharedPreferences: sharedPreferences,
127
+// userService: userService,
128
+// walletService: walletService,
129
+// walletListService: walletListService,
130
+// authenticationStore: authenticationStore);
131
+// }
132
+
133
+// if (state == AuthenticationState.authenticated ||
134
+// state == AuthenticationState.restored) {
135
+// return createDashboardPage(
136
+// walletService: walletService,
137
+// priceStore: priceStore,
138
+// trades: trades,
139
+// transactionDescriptions: transactionDescriptions,
140
+// walletStore: walletStore,
141
+// settingsStore: settingsStore);
142
+// }
143
+
144
+// if (state == AuthenticationState.created) {
145
+// return createSeedPage(
146
+// settingsStore: settingsStore,
147
+// walletService: walletService,
148
+// callback: () =>
149
+// _authenticationStore.state = AuthenticationState.authenticated);
150
+// }
151
152
return Container(color: Colors.white);
153
});
lib/src/screens/seed_language/seed_language_page.dart
+42
-52
@@ -1,3 +1,4 @@
1
+import 'package:cake_wallet/src/widgets/seed_language_selector.dart';
2
import 'package:provider/provider.dart';
3
import 'package:flutter_mobx/flutter_mobx.dart';
4
import 'package:flutter/material.dart';
@@ -11,79 +12,68 @@ import 'package:cake_wallet/src/screens/new_wallet/widgets/select_button.dart';
12
import 'package:cake_wallet/src/screens/seed_language/widgets/seed_language_picker.dart';
13
14
class SeedLanguage extends BasePage {
15
+ SeedLanguage({this.onConfirm});
16
+
17
+ final Function(BuildContext, String) onConfirm;
18
+
19
@override
15
- Widget body(BuildContext context) => SeedLanguageForm();
20
+ Widget body(BuildContext context) => SeedLanguageForm(onConfirm: onConfirm);
21
}
22
23
class SeedLanguageForm extends StatefulWidget {
24
+ SeedLanguageForm({this.onConfirm});
25
+
26
+ final Function(BuildContext, String) onConfirm;
27
+
28
@override
29
SeedLanguageFormState createState() => SeedLanguageFormState();
30
}
31
32
class SeedLanguageFormState extends State<SeedLanguageForm> {
33
static const aspectRatioImage = 1.22;
34
+
35
final walletNameImage = Image.asset('assets/images/wallet_name.png');
36
+ final _languageSelectorKey = GlobalKey<SeedLanguageSelectorState>();
37
38
@override
39
Widget build(BuildContext context) {
29
- final seedLanguageStore = Provider.of<SeedLanguageStore>(context);
30
-
31
- final List<String> seedLocales = [
32
- S.current.seed_language_english,
33
- S.current.seed_language_chinese,
34
- S.current.seed_language_dutch,
35
- S.current.seed_language_german,
36
- S.current.seed_language_japanese,
37
- S.current.seed_language_portuguese,
38
- S.current.seed_language_russian,
39
- S.current.seed_language_spanish
40
- ];
41
-
40
return Container(
41
padding: EdgeInsets.only(top: 24),
42
child: ScrollableWithBottomSection(
43
contentPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
46
- content: Column(
47
- crossAxisAlignment: CrossAxisAlignment.center,
48
- children: [
49
- Padding(
50
- padding: EdgeInsets.only(left: 12, right: 12),
51
- child: AspectRatio(
52
- aspectRatio: aspectRatioImage,
53
- child: FittedBox(child: walletNameImage, fit: BoxFit.fill)),
54
- ),
55
- Padding(padding: EdgeInsets.only(top: 40),
56
- child: Text(
57
- S.of(context).seed_language_choose,
58
- textAlign: TextAlign.center,
59
- style: TextStyle(
60
- fontSize: 16.0,
61
- fontWeight: FontWeight.w600,
62
- color: Theme.of(context).primaryTextTheme.title.color
63
- ),
64
- ),
65
- ),
66
- Padding(padding: EdgeInsets.only(top: 24),
67
- child: Observer(
68
- builder: (_) => SelectButton(
69
- image: null,
70
- text: seedLocales[seedLanguages.indexOf(seedLanguageStore.selectedSeedLanguage)],
71
- color: Theme.of(context).accentTextTheme.title.backgroundColor,
72
- textColor: Theme.of(context).primaryTextTheme.title.color,
73
- onTap: () async => await showDialog(
74
- context: context,
75
- builder: (BuildContext context) => SeedLanguagePicker()
76
- )
77
- )
78
- ),
79
- )
80
- ]),
81
- bottomSectionPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
44
+ content:
45
+ Column(crossAxisAlignment: CrossAxisAlignment.center, children: [
46
+ Padding(
47
+ padding: EdgeInsets.only(left: 12, right: 12),
48
+ child: AspectRatio(
49
+ aspectRatio: aspectRatioImage,
50
+ child: FittedBox(child: walletNameImage, fit: BoxFit.fill)),
51
+ ),
52
+ Padding(
53
+ padding: EdgeInsets.only(top: 40),
54
+ child: Text(
55
+ S.of(context).seed_language_choose,
56
+ textAlign: TextAlign.center,
57
+ style: TextStyle(
58
+ fontSize: 16.0,
59
+ fontWeight: FontWeight.w600,
60
+ color: Theme.of(context).primaryTextTheme.title.color),
61
+ ),
62
+ ),
63
+ Padding(
64
+ padding: EdgeInsets.only(top: 24),
65
+ child: SeedLanguageSelector(
66
+ key: _languageSelectorKey,
67
+ initialSelected: defaultSeedLanguage),
68
+ )
69
+ ]),
70
+ bottomSectionPadding:
71
+ EdgeInsets.only(left: 24, right: 24, bottom: 24),
72
bottomSection: Observer(
73
builder: (context) {
74
return PrimaryButton(
85
- onPressed: () =>
86
- Navigator.of(context).popAndPushNamed(seedLanguageStore.currentRoute),
75
+ onPressed: () => widget
76
+ .onConfirm(context, _languageSelectorKey.currentState.selected),
77
text: S.of(context).seed_language_next,
78
color: Colors.green,
79
textColor: Colors.white);
lib/src/screens/seed_language/widgets/seed_language_picker.dart
+101
-97
@@ -17,7 +17,7 @@ List<Image> flagImages = [
17
Image.asset('assets/images/spain.png'),
18
];
19
20
-List<String> languageCodes = [
20
+const List<String> languageCodes = [
21
'Eng',
22
'Chi',
23
'Ned',
@@ -28,19 +28,39 @@ List<String> languageCodes = [
28
'Esp',
29
];
30
31
-enum Places {topLeft, topRight, bottomLeft, bottomRight, inside}
31
+const defaultSeedLanguage = 'English';
32
+
33
+const List<String> seedLanguages = [
34
+ defaultSeedLanguage,
35
+ 'Chinese (simplified)',
36
+ 'Dutch',
37
+ 'German',
38
+ 'Japanese',
39
+ 'Portuguese',
40
+ 'Russian',
41
+ 'Spanish'
42
+];
43
+
44
+enum Places { topLeft, topRight, bottomLeft, bottomRight, inside }
45
46
class SeedLanguagePicker extends StatefulWidget {
47
+ SeedLanguagePicker({Key key, this.selected = defaultSeedLanguage})
48
+ : super(key: key);
49
+
50
+ final String selected;
51
+
52
@override
35
- SeedLanguagePickerState createState() => SeedLanguagePickerState();
53
+ SeedLanguagePickerState createState() =>
54
+ SeedLanguagePickerState(selected: selected);
55
}
56
57
class SeedLanguagePickerState extends State<SeedLanguagePicker> {
58
+ SeedLanguagePickerState({this.selected});
59
+
60
+ String selected;
61
62
@override
63
Widget build(BuildContext context) {
42
- final seedLanguageStore = Provider.of<SeedLanguageStore>(context);
43
-
64
return GestureDetector(
65
onTap: () => Navigator.of(context).pop(),
66
child: Container(
@@ -48,7 +68,8 @@ class SeedLanguagePickerState extends State<SeedLanguagePicker> {
68
child: BackdropFilter(
69
filter: ImageFilter.blur(sigmaX: 3.0, sigmaY: 3.0),
70
child: Container(
51
- decoration: BoxDecoration(color: PaletteDark.darkNightBlue.withOpacity(0.75)),
71
+ decoration: BoxDecoration(
72
+ color: PaletteDark.darkNightBlue.withOpacity(0.75)),
73
child: Center(
74
child: Column(
75
mainAxisSize: MainAxisSize.min,
@@ -62,8 +83,7 @@ class SeedLanguagePickerState extends State<SeedLanguagePicker> {
83
fontSize: 18,
84
fontWeight: FontWeight.bold,
85
decoration: TextDecoration.none,
65
- color: Colors.white
66
- ),
86
+ color: Colors.white),
87
),
88
),
89
Padding(
@@ -74,9 +94,8 @@ class SeedLanguagePickerState extends State<SeedLanguagePicker> {
94
height: 300,
95
width: 300,
96
decoration: BoxDecoration(
77
- borderRadius: BorderRadius.all(Radius.circular(14)),
78
- color: Theme.of(context).dividerColor
79
- ),
97
+ borderRadius: BorderRadius.all(Radius.circular(14)),
98
+ color: Theme.of(context).dividerColor),
99
child: GridView.count(
100
shrinkWrap: true,
101
crossAxisCount: 3,
@@ -85,71 +104,64 @@ class SeedLanguagePickerState extends State<SeedLanguagePicker> {
104
crossAxisSpacing: 1,
105
mainAxisSpacing: 1,
106
children: List.generate(9, (index) {
88
-
107
if (index == 8) {
90
-
108
return gridTile(
92
- isCurrent: false,
93
- place: Places.bottomRight,
94
- image: null,
95
- text: '',
96
- onTap: null);
97
-
109
+ isCurrent: false,
110
+ place: Places.bottomRight,
111
+ image: null,
112
+ text: '',
113
+ onTap: null);
114
} else {
99
-
115
final code = languageCodes[index];
116
final flag = flagImages[index];
102
- final isCurrent = index == seedLanguages.indexOf(seedLanguageStore.selectedSeedLanguage);
117
+ final isCurrent =
118
+ index == seedLanguages.indexOf(selected);
119
120
if (index == 0) {
121
return gridTile(
106
- isCurrent: isCurrent,
107
- place: Places.topLeft,
108
- image: flag,
109
- text: code,
110
- onTap: () {
111
- seedLanguageStore.setSelectedSeedLanguage(seedLanguages[index]);
112
- Navigator.of(context).pop();
113
- }
114
- );
122
+ isCurrent: isCurrent,
123
+ place: Places.topLeft,
124
+ image: flag,
125
+ text: code,
126
+ onTap: () {
127
+ selected = seedLanguages[index];
128
+ Navigator.of(context).pop(selected);
129
+ });
130
}
131
132
if (index == 2) {
133
return gridTile(
119
- isCurrent: isCurrent,
120
- place: Places.topRight,
121
- image: flag,
122
- text: code,
123
- onTap: () {
124
- seedLanguageStore.setSelectedSeedLanguage(seedLanguages[index]);
125
- Navigator.of(context).pop();
126
- }
127
- );
134
+ isCurrent: isCurrent,
135
+ place: Places.topRight,
136
+ image: flag,
137
+ text: code,
138
+ onTap: () {
139
+ selected = seedLanguages[index];
140
+ Navigator.of(context).pop(selected);
141
+ });
142
}
143
144
if (index == 6) {
145
return gridTile(
146
+ isCurrent: isCurrent,
147
+ place: Places.bottomLeft,
148
+ image: flag,
149
+ text: code,
150
+ onTap: () {
151
+ selected = seedLanguages[index];
152
+ Navigator.of(context).pop(selected);
153
+ });
154
+ }
155
+
156
+ return gridTile(
157
isCurrent: isCurrent,
133
- place: Places.bottomLeft,
158
+ place: Places.inside,
159
image: flag,
160
text: code,
161
onTap: () {
137
- seedLanguageStore.setSelectedSeedLanguage(seedLanguages[index]);
138
- Navigator.of(context).pop();
139
- }
140
- );
141
- }
142
-
143
- return gridTile(
144
- isCurrent: isCurrent,
145
- place: Places.inside,
146
- image: flag,
147
- text: code,
148
- onTap: () {
149
- seedLanguageStore.setSelectedSeedLanguage(seedLanguages[index]);
150
- Navigator.of(context).pop();
151
- }
152
- );
162
+ selected = seedLanguages[index];
163
+ Navigator.of(context).pop(selected);
164
+ });
165
}
166
}),
167
),
@@ -165,13 +177,12 @@ class SeedLanguagePickerState extends State<SeedLanguagePicker> {
177
);
178
}
179
168
- Widget gridTile({
169
- @required bool isCurrent,
170
- @required Places place,
171
- @required Image image,
172
- @required String text,
173
- @required VoidCallback onTap}) {
174
-
180
+ Widget gridTile(
181
+ {@required bool isCurrent,
182
+ @required Places place,
183
+ @required Image image,
184
+ @required String text,
185
+ @required VoidCallback onTap}) {
186
BorderRadius borderRadius;
187
final color = isCurrent
188
? Theme.of(context).accentTextTheme.subtitle.decorationColor
@@ -199,40 +210,33 @@ class SeedLanguagePickerState extends State<SeedLanguagePicker> {
210
}
211
212
return GestureDetector(
202
- onTap: onTap,
203
- child: Container(
204
- padding: EdgeInsets.all(10),
205
- decoration: BoxDecoration(
206
- borderRadius: borderRadius,
207
- color: color
208
- ),
209
- child: Center(
210
- child: Row(
211
- mainAxisSize: MainAxisSize.min,
212
- mainAxisAlignment: MainAxisAlignment.center,
213
- crossAxisAlignment: CrossAxisAlignment.center,
214
- children: <Widget>[
215
- image != null
216
- ? image
217
- : Offstage(),
218
- Padding(
219
- padding: image != null
220
- ? EdgeInsets.only(left: 10)
221
- : EdgeInsets.only(left: 0),
222
- child: Text(
223
- text,
224
- style: TextStyle(
225
- fontSize: 18,
226
- fontWeight: FontWeight.bold,
227
- decoration: TextDecoration.none,
228
- color: textColor
213
+ onTap: onTap,
214
+ child: Container(
215
+ padding: EdgeInsets.all(10),
216
+ decoration: BoxDecoration(borderRadius: borderRadius, color: color),
217
+ child: Center(
218
+ child: Row(
219
+ mainAxisSize: MainAxisSize.min,
220
+ mainAxisAlignment: MainAxisAlignment.center,
221
+ crossAxisAlignment: CrossAxisAlignment.center,
222
+ children: <Widget>[
223
+ image != null ? image : Offstage(),
224
+ Padding(
225
+ padding: image != null
226
+ ? EdgeInsets.only(left: 10)
227
+ : EdgeInsets.only(left: 0),
228
+ child: Text(
229
+ text,
230
+ style: TextStyle(
231
+ fontSize: 18,
232
+ fontWeight: FontWeight.bold,
233
+ decoration: TextDecoration.none,
234
+ color: textColor),
235
),
230
- ),
231
- )
232
- ],
236
+ )
237
+ ],
238
+ ),
239
),
234
- ),
235
- )
236
- );
240
+ ));
241
}
238
-}
\ No newline at end of file
242
+}
lib/src/screens/subaddress/address_edit_or_create_page.dart
new
+75
@@ -0,0 +1,75 @@
1
+import 'package:mobx/mobx.dart';
2
+import 'package:flutter/cupertino.dart';
3
+import 'package:flutter/material.dart';
4
+import 'package:flutter_mobx/flutter_mobx.dart';
5
+import 'package:cake_wallet/generated/i18n.dart';
6
+import 'package:cake_wallet/view_model/address_list/address_edit_or_create_view_model.dart';
7
+import 'package:cake_wallet/core/AddressLabelValidator.dart';
8
+import 'package:cake_wallet/src/widgets/primary_button.dart';
9
+import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
10
+import 'package:cake_wallet/src/screens/base_page.dart';
11
+
12
+class AddressEditOrCreatePage extends BasePage {
13
+ AddressEditOrCreatePage({@required this.addressEditOrCreateViewModel})
14
+ : _formKey = GlobalKey<FormState>(),
15
+ _labelController = TextEditingController(),
16
+ super() {
17
+ _labelController.addListener(
18
+ () => addressEditOrCreateViewModel.label = _labelController.text);
19
+ _labelController.text = addressEditOrCreateViewModel.label;
20
+ print(_labelController.text);
21
+ print(addressEditOrCreateViewModel.label);
22
+ }
23
+
24
+ final AddressEditOrCreateViewModel addressEditOrCreateViewModel;
25
+ final GlobalKey<FormState> _formKey;
26
+ final TextEditingController _labelController;
27
+
28
+ @override
29
+ String get title => S.current.new_subaddress_title;
30
+
31
+ @override
32
+ Widget body(BuildContext context) {
33
+ reaction((_) => addressEditOrCreateViewModel.state,
34
+ (AddressEditOrCreateState state) {
35
+ if (state is AddressSavedSuccessfully) {
36
+ WidgetsBinding.instance
37
+ .addPostFrameCallback((_) => Navigator.of(context).pop());
38
+ }
39
+ });
40
+
41
+ return Form(
42
+ key: _formKey,
43
+ child: Container(
44
+ padding: EdgeInsets.all(24.0),
45
+ child: Column(
46
+ children: <Widget>[
47
+ Expanded(
48
+ child: Center(
49
+ child: BaseTextFormField(
50
+ controller: _labelController,
51
+ hintText: S.of(context).new_subaddress_label_name,
52
+ validator: AddressLabelValidator()))),
53
+ Observer(
54
+ builder: (_) => LoadingPrimaryButton(
55
+ onPressed: () async {
56
+ if (_formKey.currentState.validate()) {
57
+ await addressEditOrCreateViewModel.save();
58
+ }
59
+ },
60
+ text: addressEditOrCreateViewModel.isEdit
61
+ ? S.of(context).rename
62
+ : S.of(context).new_subaddress_create,
63
+ color: Colors.green,
64
+ textColor: Colors.white,
65
+ isLoading:
66
+ addressEditOrCreateViewModel.state is AddressIsSaving,
67
+ isDisabled:
68
+ addressEditOrCreateViewModel.label?.isEmpty ?? true,
69
+ ),
70
+ )
71
+ ],
72
+ ),
73
+ ));
74
+ }
75
+}
\ No newline at end of file
lib/src/screens/subaddress/new_subaddress_page.dart
deleted
-130
@@ -1,130 +0,0 @@
1
-import 'package:cake_wallet/src/domain/monero/subaddress.dart';
2
-import 'package:mobx/mobx.dart';
3
-import 'package:provider/provider.dart';
4
-import 'package:flutter/cupertino.dart';
5
-import 'package:flutter/material.dart';
6
-import 'package:flutter_mobx/flutter_mobx.dart';
7
-import 'package:cake_wallet/generated/i18n.dart';
8
-import 'package:cake_wallet/src/stores/subaddress_creation/subaddress_creation_state.dart';
9
-import 'package:cake_wallet/src/stores/subaddress_creation/subaddress_creation_store.dart';
10
-import 'package:cake_wallet/src/widgets/primary_button.dart';
11
-import 'package:cake_wallet/src/screens/base_page.dart';
12
-import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
13
-
14
-class NewSubaddressPage extends BasePage {
15
- NewSubaddressPage({this.subaddress});
16
-
17
- final Subaddress subaddress;
18
-
19
- @override
20
- String get title => S.current.new_subaddress_title;
21
-
22
- @override
23
- Widget body(BuildContext context) => NewSubaddressForm(subaddress);
24
-
25
- @override
26
- Widget build(BuildContext context) {
27
- final subaddressCreationStore =
28
- Provider.of<SubadrressCreationStore>(context);
29
-
30
- reaction((_) => subaddressCreationStore.state, (SubaddressCreationState state) {
31
- if (state is SubaddressCreatedSuccessfully) {
32
- WidgetsBinding.instance
33
- .addPostFrameCallback((_) => Navigator.of(context).pop());
34
- }
35
- });
36
-
37
- return super.build(context);
38
- }
39
-}
40
-
41
-class NewSubaddressForm extends StatefulWidget {
42
- NewSubaddressForm(this.subaddress);
43
-
44
- final Subaddress subaddress;
45
-
46
- @override
47
- NewSubaddressFormState createState() => NewSubaddressFormState(subaddress);
48
-}
49
-
50
-class NewSubaddressFormState extends State<NewSubaddressForm> {
51
- NewSubaddressFormState(this.subaddress);
52
-
53
- final _formKey = GlobalKey<FormState>();
54
- final _labelController = TextEditingController();
55
- final Subaddress subaddress;
56
-
57
- @override
58
- void initState() {
59
- if (subaddress != null) _labelController.text = subaddress.label;
60
- super.initState();
61
- }
62
-
63
- @override
64
- void dispose() {
65
- _labelController.dispose();
66
- super.dispose();
67
- }
68
-
69
- @override
70
- Widget build(BuildContext context) {
71
- final subaddressCreationStore =
72
- Provider.of<SubadrressCreationStore>(context);
73
-
74
- _labelController.addListener(() {
75
- if (_labelController.text.isNotEmpty) {
76
- subaddressCreationStore.setDisabledStatus(false);
77
- } else {
78
- subaddressCreationStore.setDisabledStatus(true);
79
- }
80
- });
81
-
82
- return Form(
83
- key: _formKey,
84
- child: Container(
85
- padding: EdgeInsets.all(24.0),
86
- child: Column(
87
- children: <Widget>[
88
- Expanded(
89
- child: Center(
90
- child: BaseTextFormField(
91
- controller: _labelController,
92
- hintText: S.of(context).new_subaddress_label_name,
93
- validator: (value) {
94
- subaddressCreationStore.validateSubaddressName(value);
95
- return subaddressCreationStore.errorMessage;
96
- }
97
- )
98
- )
99
- ),
100
- Observer(
101
- builder: (_) => LoadingPrimaryButton(
102
- onPressed: () async {
103
- if (_formKey.currentState.validate()) {
104
- if (subaddress != null) {
105
- await subaddressCreationStore.setLabel(
106
- addressIndex: subaddress.id,
107
- label: _labelController.text
108
- );
109
- } else {
110
- await subaddressCreationStore.add(
111
- label: _labelController.text);
112
- }
113
- }
114
- },
115
- text: subaddress != null
116
- ? S.of(context).rename
117
- : S.of(context).new_subaddress_create,
118
- color: Colors.green,
119
- textColor: Colors.white,
120
- isLoading:
121
- subaddressCreationStore.state is SubaddressIsCreating,
122
- isDisabled: subaddressCreationStore.isDisabledStatus,
123
- ),
124
- )
125
- ],
126
- ),
127
- )
128
- );
129
- }
130
-}
lib/src/screens/subaddress/subaddress_list_page.dart
+2
-6
@@ -32,9 +32,7 @@ class SubaddressListPage extends BasePage {
32
child: Observer(
33
builder: (_) => ListView.separated(
34
separatorBuilder: (_, __) => Divider(
35
- color: Theme.of(context).dividerTheme.color,
36
- height: 1.0,
37
- ),
35
+ color: Theme.of(context).dividerTheme.color, height: 1.0),
36
itemCount: subaddressListStore.subaddresses == null
37
? 0
38
: subaddressListStore.subaddresses.length,
@@ -42,9 +40,7 @@ class SubaddressListPage extends BasePage {
40
final subaddress = subaddressListStore.subaddresses[index];
41
final isCurrent =
42
walletStore.subaddress.address == subaddress.address;
45
- final label = subaddress.label != null
46
- ? subaddress.label
47
- : subaddress.address;
43
+ final label = subaddress.label ?? subaddress.address;
44
45
return InkWell(
46
onTap: () => Navigator.of(context).pop(subaddress),
lib/src/stores/auth/auth_store.dart
+99
-99
@@ -1,99 +1,99 @@
1
-import 'dart:async';
2
-import 'package:flutter/foundation.dart';
3
-import 'package:shared_preferences/shared_preferences.dart';
4
-import 'package:mobx/mobx.dart';
5
-import 'package:cake_wallet/src/domain/services/user_service.dart';
6
-import 'package:cake_wallet/src/domain/services/wallet_service.dart';
7
-import 'package:cake_wallet/src/stores/auth/auth_state.dart';
8
-import 'package:cake_wallet/generated/i18n.dart';
9
-
10
-part 'auth_store.g.dart';
11
-
12
-class AuthStore = AuthStoreBase with _$AuthStore;
13
-
14
-abstract class AuthStoreBase with Store {
15
- AuthStoreBase(
16
- {@required this.userService,
17
- @required this.walletService,
18
- @required this.sharedPreferences}) {
19
- state = AuthenticationStateInitial();
20
- _failureCounter = 0;
21
- }
22
-
23
- static const maxFailedLogins = 3;
24
- static const banTimeout = 180; // 3 mins
25
- final banTimeoutKey = S.current.auth_store_ban_timeout;
26
-
27
- final UserService userService;
28
- final WalletService walletService;
29
-
30
- final SharedPreferences sharedPreferences;
31
-
32
- @observable
33
- AuthState state;
34
-
35
- @observable
36
- int _failureCounter;
37
-
38
- @action
39
- Future auth({String password}) async {
40
- state = AuthenticationStateInitial();
41
- final _banDuration = banDuration();
42
-
43
- if (_banDuration != null) {
44
- state = AuthenticationBanned(
45
- error: S.current.auth_store_banned_for + '${_banDuration.inMinutes}' + S.current.auth_store_banned_minutes);
46
- return;
47
- }
48
-
49
- state = AuthenticationInProgress();
50
- final isAuth = await userService.authenticate(password);
51
-
52
- if (isAuth) {
53
- state = AuthenticatedSuccessfully();
54
- _failureCounter = 0;
55
- } else {
56
- _failureCounter += 1;
57
-
58
- if (_failureCounter >= maxFailedLogins) {
59
- final banDuration = await ban();
60
- state = AuthenticationBanned(
61
- error: S.current.auth_store_banned_for + '${banDuration.inMinutes}' + S.current.auth_store_banned_minutes);
62
- return;
63
- }
64
-
65
- state = AuthenticationFailure(error: S.current.auth_store_incorrect_password);
66
- }
67
- }
68
-
69
- Duration banDuration() {
70
- final unbanTimestamp = sharedPreferences.getInt(banTimeoutKey);
71
-
72
- if (unbanTimestamp == null) {
73
- return null;
74
- }
75
-
76
- final unbanTime = DateTime.fromMillisecondsSinceEpoch(unbanTimestamp);
77
- final now = DateTime.now();
78
-
79
- if (now.isAfter(unbanTime)) {
80
- return null;
81
- }
82
-
83
- return Duration(milliseconds: unbanTimestamp - now.millisecondsSinceEpoch);
84
- }
85
-
86
- Future<Duration> ban() async {
87
- final multiplier = _failureCounter - maxFailedLogins + 1;
88
- final timeout = (multiplier * banTimeout) * 1000;
89
- final unbanTimestamp = DateTime.now().millisecondsSinceEpoch + timeout;
90
- await sharedPreferences.setInt(banTimeoutKey, unbanTimestamp);
91
-
92
- return Duration(milliseconds: timeout);
93
- }
94
-
95
- @action
96
- void biometricAuth() {
97
- state = AuthenticatedSuccessfully();
98
- }
99
-}
1
+//import 'dart:async';
2
+//import 'package:flutter/foundation.dart';
3
+//import 'package:shared_preferences/shared_preferences.dart';
4
+//import 'package:mobx/mobx.dart';
5
+//import 'package:cake_wallet/src/domain/services/user_service.dart';
6
+//import 'package:cake_wallet/src/domain/services/wallet_service.dart';
7
+//import 'package:cake_wallet/view_model/auth_state.dart';
8
+//import 'package:cake_wallet/generated/i18n.dart';
9
+//
10
+//part 'auth_store.g.dart';
11
+//
12
+//class AuthStore = AuthStoreBase with _$AuthStore;
13
+//
14
+//abstract class AuthStoreBase with Store {
15
+// AuthStoreBase(
16
+// {@required this.userService,
17
+// @required this.walletService,
18
+// @required this.sharedPreferences}) {
19
+// state = AuthenticationStateInitial();
20
+// _failureCounter = 0;
21
+// }
22
+//
23
+// static const maxFailedLogins = 3;
24
+// static const banTimeout = 180; // 3 mins
25
+// final banTimeoutKey = S.current.auth_store_ban_timeout;
26
+//
27
+// final UserService userService;
28
+// final WalletService walletService;
29
+//
30
+// final SharedPreferences sharedPreferences;
31
+//
32
+// @observable
33
+// AuthState state;
34
+//
35
+// @observable
36
+// int _failureCounter;
37
+//
38
+// @action
39
+// Future auth({String password}) async {
40
+// state = AuthenticationStateInitial();
41
+// final _banDuration = banDuration();
42
+//
43
+// if (_banDuration != null) {
44
+// state = AuthenticationBanned(
45
+// error: S.current.auth_store_banned_for + '${_banDuration.inMinutes}' + S.current.auth_store_banned_minutes);
46
+// return;
47
+// }
48
+//
49
+// state = AuthenticationInProgress();
50
+// final isAuth = await userService.authenticate(password);
51
+//
52
+// if (isAuth) {
53
+// state = AuthenticatedSuccessfully();
54
+// _failureCounter = 0;
55
+// } else {
56
+// _failureCounter += 1;
57
+//
58
+// if (_failureCounter >= maxFailedLogins) {
59
+// final banDuration = await ban();
60
+// state = AuthenticationBanned(
61
+// error: S.current.auth_store_banned_for + '${banDuration.inMinutes}' + S.current.auth_store_banned_minutes);
62
+// return;
63
+// }
64
+//
65
+// state = AuthenticationFailure(error: S.current.auth_store_incorrect_password);
66
+// }
67
+// }
68
+//
69
+// Duration banDuration() {
70
+// final unbanTimestamp = sharedPreferences.getInt(banTimeoutKey);
71
+//
72
+// if (unbanTimestamp == null) {
73
+// return null;
74
+// }
75
+//
76
+// final unbanTime = DateTime.fromMillisecondsSinceEpoch(unbanTimestamp);
77
+// final now = DateTime.now();
78
+//
79
+// if (now.isAfter(unbanTime)) {
80
+// return null;
81
+// }
82
+//
83
+// return Duration(milliseconds: unbanTimestamp - now.millisecondsSinceEpoch);
84
+// }
85
+//
86
+// Future<Duration> ban() async {
87
+// final multiplier = _failureCounter - maxFailedLogins + 1;
88
+// final timeout = (multiplier * banTimeout) * 1000;
89
+// final unbanTimestamp = DateTime.now().millisecondsSinceEpoch + timeout;
90
+// await sharedPreferences.setInt(banTimeoutKey, unbanTimestamp);
91
+//
92
+// return Duration(milliseconds: timeout);
93
+// }
94
+//
95
+// @action
96
+// void biometricAuth() {
97
+// state = AuthenticatedSuccessfully();
98
+// }
99
+//}
lib/src/stores/authentication/authentication_store.dart
+2
-2
@@ -30,8 +30,8 @@ abstract class AuthenticationStoreBase with Store {
30
@observable
31
AuthenticationState state;
32
33
- @observable
34
- String errorMessage;
33
+// @observable
34
+// String errorMessage;
35
36
Future started() async {
37
final canAuth = await userService.canAuthenticate();
lib/src/stores/wallet_restoration/wallet_restoration_store.dart
+28
-28
@@ -2,7 +2,7 @@ import 'package:mobx/mobx.dart';
2
import 'package:flutter/foundation.dart';
3
import 'package:shared_preferences/shared_preferences.dart';
4
import 'package:cake_wallet/src/domain/services/wallet_list_service.dart';
5
-import 'package:cake_wallet/src/domain/common/mnemotic_item.dart';
5
+import 'package:cake_wallet/src/domain/common/mnemonic_item.dart';
6
import 'package:cake_wallet/src/stores/wallet_restoration/wallet_restoration_state.dart';
7
import 'package:cake_wallet/src/stores/authentication/authentication_store.dart';
8
import 'package:cake_wallet/src/domain/common/crypto_currency.dart';
@@ -37,7 +37,7 @@ abstract class WalleRestorationStoreBase with Store {
37
bool isValid;
38
39
@observable
40
- List<MnemoticItem> seed;
40
+ List<MnemonicItem> seed;
41
42
@observable
43
bool disabledState;
@@ -79,35 +79,35 @@ abstract class WalleRestorationStoreBase with Store {
79
}
80
81
@action
82
- void setSeed(List<MnemoticItem> seed) {
82
+ void setSeed(List<MnemonicItem> seed) {
83
this.seed = seed;
84
}
85
86
- @action
87
- void validateSeed(List<MnemoticItem> seed) {
88
- final _seed = seed != null ? seed : this.seed;
89
- bool isValid = _seed != null ? _seed.length == 25 : false;
90
-
91
- if (!isValid) {
92
- errorMessage = S.current.wallet_restoration_store_incorrect_seed_length;
93
- this.isValid = isValid;
94
- return;
95
- }
96
-
97
- for (final item in _seed) {
98
- if (!item.isCorrect()) {
99
- isValid = false;
100
- break;
101
- }
102
- }
103
-
104
- if (isValid) {
105
- errorMessage = null;
106
- }
107
-
108
- this.isValid = isValid;
109
- return;
110
- }
86
+// @action
87
+// void validateSeed(List<MnemonicItem> seed) {
88
+// final _seed = seed != null ? seed : this.seed;
89
+// bool isValid = _seed != null ? _seed.length == 25 : false;
90
+//
91
+// if (!isValid) {
92
+// errorMessage = S.current.wallet_restoration_store_incorrect_seed_length;
93
+// this.isValid = isValid;
94
+// return;
95
+// }
96
+//
97
+// for (final item in _seed) {
98
+// if (!item.isCorrect()) {
99
+// isValid = false;
100
+// break;
101
+// }
102
+// }
103
+//
104
+// if (isValid) {
105
+// errorMessage = null;
106
+// }
107
+//
108
+// this.isValid = isValid;
109
+// return;
110
+// }
111
112
String _seedText() {
113
return seed.fold('', (acc, item) => acc + ' ' + item.toString());
lib/src/widgets/base_text_form_field.dart
+39
-43
@@ -2,24 +2,24 @@ import 'package:flutter/material.dart';
2
import 'package:flutter/services.dart';
3
4
class BaseTextFormField extends StatelessWidget {
5
- BaseTextFormField({
6
- this.controller,
7
- this.keyboardType = TextInputType.text,
8
- this.textInputAction = TextInputAction.done,
9
- this.textAlign = TextAlign.start,
10
- this.autovalidate = false,
11
- this.hintText = '',
12
- this.maxLines = 1,
13
- this.inputFormatters,
14
- this.textColor,
15
- this.hintColor,
16
- this.borderColor,
17
- this.prefix,
18
- this.suffix,
19
- this.suffixIcon,
20
- this.enabled = true,
21
- this.validator
22
- });
5
+ BaseTextFormField(
6
+ {this.controller,
7
+ this.keyboardType = TextInputType.text,
8
+ this.textInputAction = TextInputAction.done,
9
+ this.textAlign = TextAlign.start,
10
+ this.autovalidate = false,
11
+ this.hintText = '',
12
+ this.maxLines = 1,
13
+ this.inputFormatters,
14
+ this.textColor,
15
+ this.hintColor,
16
+ this.borderColor,
17
+ this.prefix,
18
+ this.suffix,
19
+ this.suffixIcon,
20
+ this.enabled = true,
21
+ this.validator,
22
+ this.placeholderTextStyle});
23
24
final TextEditingController controller;
25
final TextInputType keyboardType;
@@ -37,6 +37,7 @@ class BaseTextFormField extends StatelessWidget {
37
final Widget suffixIcon;
38
final bool enabled;
39
final FormFieldValidator<String> validator;
40
+ final TextStyle placeholderTextStyle;
41
42
@override
43
Widget build(BuildContext context) {
@@ -50,32 +51,27 @@ class BaseTextFormField extends StatelessWidget {
51
inputFormatters: inputFormatters,
52
enabled: enabled,
53
style: TextStyle(
53
- fontSize: 16.0,
54
- color: textColor ?? Theme.of(context).primaryTextTheme.title.color
55
- ),
54
+ fontSize: 16.0,
55
+ color: textColor ?? Theme.of(context).primaryTextTheme.title.color),
56
decoration: InputDecoration(
57
- prefix: prefix,
58
- suffix: suffix,
59
- suffixIcon: suffixIcon,
60
- hintStyle: TextStyle(
61
- color: hintColor ?? Theme.of(context).primaryTextTheme.caption.color,
62
- fontSize: 16
63
- ),
64
- hintText: hintText,
65
- focusedBorder: UnderlineInputBorder(
66
- borderSide: BorderSide(
67
- color: borderColor ?? Theme.of(context).dividerColor,
68
- width: 1.0
69
- )
70
- ),
71
- enabledBorder: UnderlineInputBorder(
72
- borderSide: BorderSide(
73
- color: borderColor ?? Theme.of(context).dividerColor,
74
- width: 1.0
75
- )
76
- )
77
- ),
57
+ prefix: prefix,
58
+ suffix: suffix,
59
+ suffixIcon: suffixIcon,
60
+ hintStyle: placeholderTextStyle ??
61
+ TextStyle(
62
+ color: hintColor ??
63
+ Theme.of(context).primaryTextTheme.caption.color,
64
+ fontSize: 16),
65
+ hintText: hintText,
66
+ focusedBorder: UnderlineInputBorder(
67
+ borderSide: BorderSide(
68
+ color: borderColor ?? Theme.of(context).dividerColor,
69
+ width: 1.0)),
70
+ enabledBorder: UnderlineInputBorder(
71
+ borderSide: BorderSide(
72
+ color: borderColor ?? Theme.of(context).dividerColor,
73
+ width: 1.0))),
74
validator: validator,
75
);
76
}
81
-}
\ No newline at end of file
77
+}
lib/src/widgets/blockchain_height_widget.dart
+30
-16
@@ -4,7 +4,10 @@ import 'package:cake_wallet/generated/i18n.dart';
4
import 'package:cake_wallet/src/domain/monero/get_height_by_date.dart';
5
6
class BlockchainHeightWidget extends StatefulWidget {
7
- BlockchainHeightWidget({GlobalKey key}) : super(key: key);
7
+ BlockchainHeightWidget({GlobalKey key, this.onHeightChange})
8
+ : super(key: key);
9
+
10
+ final Function(int) onHeightChange;
11
12
@override
13
State<StatefulWidget> createState() => BlockchainHeightState();
@@ -13,15 +16,23 @@ class BlockchainHeightWidget extends StatefulWidget {
16
class BlockchainHeightState extends State<BlockchainHeightWidget> {
17
final dateController = TextEditingController();
18
final restoreHeightController = TextEditingController();
19
+
20
int get height => _height;
21
int _height = 0;
22
23
@override
24
void initState() {
21
- restoreHeightController.addListener(() => _height =
22
- restoreHeightController.text != null
25
+ restoreHeightController.addListener(() {
26
+ try {
27
+ _changeHeight(restoreHeightController.text != null &&
28
+ restoreHeightController.text.isNotEmpty
29
? int.parse(restoreHeightController.text)
30
: 0);
31
+ } catch (_) {
32
+ _changeHeight(0);
33
+ }
34
+ });
35
+
36
super.initState();
37
}
38
@@ -38,21 +49,18 @@ class BlockchainHeightState extends State<BlockchainHeightWidget> {
49
child: TextFormField(
50
style: TextStyle(
51
fontSize: 16.0,
41
- color: Theme.of(context).primaryTextTheme.title.color
42
- ),
52
+ color: Theme.of(context).primaryTextTheme.title.color),
53
controller: restoreHeightController,
54
keyboardType: TextInputType.numberWithOptions(
55
signed: false, decimal: false),
56
decoration: InputDecoration(
57
hintStyle: TextStyle(
58
color: Theme.of(context).primaryTextTheme.caption.color,
49
- fontSize: 16
50
- ),
59
+ fontSize: 16),
60
hintText: S.of(context).widgets_restore_from_blockheight,
61
focusedBorder: UnderlineInputBorder(
62
borderSide: BorderSide(
54
- color: Theme.of(context).dividerColor,
55
- width: 1.0)),
63
+ color: Theme.of(context).dividerColor, width: 1.0)),
64
enabledBorder: UnderlineInputBorder(
65
borderSide: BorderSide(
66
color: Theme.of(context).dividerColor,
@@ -81,13 +89,14 @@ class BlockchainHeightState extends State<BlockchainHeightWidget> {
89
child: TextFormField(
90
style: TextStyle(
91
fontSize: 16.0,
84
- color: Theme.of(context).primaryTextTheme.title.color
85
- ),
92
+ color: Theme.of(context).primaryTextTheme.title.color),
93
decoration: InputDecoration(
94
hintStyle: TextStyle(
88
- color: Theme.of(context).primaryTextTheme.caption.color,
89
- fontSize: 16
90
- ),
95
+ color: Theme.of(context)
96
+ .primaryTextTheme
97
+ .caption
98
+ .color,
99
+ fontSize: 16),
100
hintText: S.of(context).widgets_restore_from_date,
101
focusedBorder: UnderlineInputBorder(
102
borderSide: BorderSide(
@@ -113,7 +122,7 @@ class BlockchainHeightState extends State<BlockchainHeightWidget> {
122
123
Future _selectDate(BuildContext context) async {
124
final now = DateTime.now();
116
- final DateTime date = await showDatePicker(
125
+ final date = await showDatePicker(
126
context: context,
127
initialDate: now.subtract(Duration(days: 1)),
128
firstDate: DateTime(2014, DateTime.april),
@@ -125,8 +134,13 @@ class BlockchainHeightState extends State<BlockchainHeightWidget> {
134
setState(() {
135
dateController.text = DateFormat('yyyy-MM-dd').format(date);
136
restoreHeightController.text = '$height';
128
- _height = height;
137
+ _changeHeight(height);
138
});
139
}
140
}
141
+
142
+ void _changeHeight(int height) {
143
+ _height = height;
144
+ widget.onHeightChange?.call(height);
145
+ }
146
}
lib/src/widgets/seed_language_selector.dart
+47
@@ -0,0 +1,47 @@
1
+import 'package:flutter/material.dart';
2
+import 'package:cake_wallet/generated/i18n.dart';
3
+import 'package:cake_wallet/src/screens/new_wallet/widgets/select_button.dart';
4
+import 'package:cake_wallet/src/screens/seed_language/widgets/seed_language_picker.dart';
5
+
6
+class SeedLanguageSelector extends StatefulWidget {
7
+ SeedLanguageSelector({Key key, this.initialSelected}) : super(key: key);
8
+
9
+ final String initialSelected;
10
+
11
+ @override
12
+ SeedLanguageSelectorState createState() =>
13
+ SeedLanguageSelectorState(selected: initialSelected);
14
+}
15
+
16
+class SeedLanguageSelectorState extends State<SeedLanguageSelector> {
17
+ SeedLanguageSelectorState({this.selected});
18
+
19
+ final seedLocales = [
20
+ S.current.seed_language_english,
21
+ S.current.seed_language_chinese,
22
+ S.current.seed_language_dutch,
23
+ S.current.seed_language_german,
24
+ S.current.seed_language_japanese,
25
+ S.current.seed_language_portuguese,
26
+ S.current.seed_language_russian,
27
+ S.current.seed_language_spanish
28
+ ];
29
+ String selected;
30
+ final _pickerKey = GlobalKey<SeedLanguagePickerState>();
31
+
32
+ @override
33
+ Widget build(BuildContext context) {
34
+ return SelectButton(
35
+ image: null,
36
+ text: seedLocales[seedLanguages.indexOf(selected)],
37
+ color: Theme.of(context).accentTextTheme.title.backgroundColor,
38
+ textColor: Theme.of(context).primaryTextTheme.title.color,
39
+ onTap: () async {
40
+ final selected = await showDialog<String>(
41
+ context: context,
42
+ builder: (BuildContext context) =>
43
+ SeedLanguagePicker(key: _pickerKey, selected: this.selected));
44
+ setState(() => this.selected = selected);
45
+ });
46
+ }
47
+}
lib/src/widgets/seed_widget.dart
+237
-258
@@ -1,94 +1,64 @@
1
-import 'package:cake_wallet/src/widgets/primary_button.dart';
1
import 'package:flutter/cupertino.dart';
2
import 'package:flutter/material.dart';
3
import 'package:flutter/services.dart';
4
import 'package:cake_wallet/palette.dart';
6
-import 'package:cake_wallet/src/domain/monero/mnemonics/english.dart';
7
-import 'package:cake_wallet/src/domain/monero/mnemonics/chinese_simplified.dart';
8
-import 'package:cake_wallet/src/domain/monero/mnemonics/dutch.dart';
9
-import 'package:cake_wallet/src/domain/monero/mnemonics/german.dart';
10
-import 'package:cake_wallet/src/domain/monero/mnemonics/japanese.dart';
11
-import 'package:cake_wallet/src/domain/monero/mnemonics/portuguese.dart';
12
-import 'package:cake_wallet/src/domain/monero/mnemonics/russian.dart';
13
-import 'package:cake_wallet/src/domain/monero/mnemonics/spanish.dart';
14
-import 'package:cake_wallet/src/domain/common/mnemotic_item.dart';
5
+import 'package:cake_wallet/core/seed_validator.dart';
6
+import 'package:cake_wallet/src/widgets/primary_button.dart';
7
+import 'package:cake_wallet/src/domain/common/mnemonic_item.dart';
8
import 'package:cake_wallet/generated/i18n.dart';
9
10
class SeedWidget extends StatefulWidget {
18
- SeedWidget({Key key, this.onMnemoticChange, this.onFinish, this.seedLanguage}) : super(key: key) {
19
- switch (seedLanguage) {
20
- case 'English':
21
- words = EnglishMnemonics.words;
22
- break;
23
- case 'Chinese (simplified)':
24
- words = ChineseSimplifiedMnemonics.words;
25
- break;
26
- case 'Dutch':
27
- words = DutchMnemonics.words;
28
- break;
29
- case 'German':
30
- words = GermanMnemonics.words;
31
- break;
32
- case 'Japanese':
33
- words = JapaneseMnemonics.words;
34
- break;
35
- case 'Portuguese':
36
- words = PortugueseMnemonics.words;
37
- break;
38
- case 'Russian':
39
- words = RussianMnemonics.words;
40
- break;
41
- case 'Spanish':
42
- words = SpanishMnemonics.words;
43
- break;
44
- default:
45
- words = EnglishMnemonics.words;
46
- }
47
- }
48
-
49
- final Function(List<MnemoticItem>) onMnemoticChange;
11
+ SeedWidget(
12
+ {Key key,
13
+ this.maxLength,
14
+ this.onMnemonicChange,
15
+ this.onFinish,
16
+ this.validator})
17
+ : super(key: key);
18
+
19
+ final int maxLength;
20
+ final Function(List<MnemonicItem>) onMnemonicChange;
21
final Function() onFinish;
51
- final String seedLanguage;
52
- List<String> words;
22
+ final SeedValidator validator;
23
24
@override
55
- SeedWidgetState createState() => SeedWidgetState();
25
+ SeedWidgetState createState() => SeedWidgetState(maxLength: maxLength);
26
}
27
28
class SeedWidgetState extends State<SeedWidget> {
59
- static const maxLength = 25;
29
+ SeedWidgetState({this.maxLength});
30
61
- List<MnemoticItem> items = <MnemoticItem>[];
31
+ List<MnemonicItem> items = <MnemonicItem>[];
32
+ final int maxLength;
33
final _seedController = TextEditingController();
34
final _seedTextFieldKey = GlobalKey();
64
- MnemoticItem selectedItem;
35
+ MnemonicItem selectedItem;
36
bool isValid;
37
String errorMessage;
38
68
- List<MnemoticItem> currentMnemotics;
69
- bool isCurrentMnemoticValid;
39
+ List<MnemonicItem> currentMnemonics;
40
+ bool isCurrentMnemonicValid;
41
String _errorMessage;
42
43
@override
44
void initState() {
45
super.initState();
46
isValid = false;
76
- isCurrentMnemoticValid = false;
47
+ isCurrentMnemonicValid = false;
48
_seedController
78
- .addListener(() => changeCurrentMnemotic(_seedController.text));
49
+ .addListener(() => changeCurrentMnemonic(_seedController.text));
50
}
51
81
- void addMnemotic(String text) {
82
- setState(() => items.add(MnemoticItem(
83
- text: text.trim().toLowerCase(), dic: widget.words)));
52
+ void addMnemonic(String text) {
53
+ setState(() => items.add(MnemonicItem(text: text.trim().toLowerCase())));
54
_seedController.text = '';
55
86
- if (widget.onMnemoticChange != null) {
87
- widget.onMnemoticChange(items);
56
+ if (widget.onMnemonicChange != null) {
57
+ widget.onMnemonicChange(items);
58
}
59
}
60
91
- void mnemoticFromText(String text) {
61
+ void mnemonicFromText(String text) {
62
final splitted = text.split(' ');
63
64
if (splitted.length >= 2) {
@@ -98,18 +68,18 @@ class SeedWidgetState extends State<SeedWidget> {
68
}
69
70
if (selectedItem != null) {
101
- editTextOfSelectedMnemotic(text);
71
+ editTextOfSelectedMnemonic(text);
72
} else {
103
- addMnemotic(text);
73
+ addMnemonic(text);
74
}
75
}
76
}
77
}
78
109
- void selectMnemotic(MnemoticItem item) {
79
+ void selectMnemonic(MnemonicItem item) {
80
setState(() {
81
selectedItem = item;
112
- currentMnemotics = [item];
82
+ currentMnemonics = [item];
83
84
_seedController
85
..text = item.text
@@ -117,23 +87,23 @@ class SeedWidgetState extends State<SeedWidget> {
87
});
88
}
89
120
- void onMnemoticTap(MnemoticItem item) {
90
+ void onMnemonicTap(MnemonicItem item) {
91
if (selectedItem == item) {
92
setState(() => selectedItem = null);
93
_seedController.text = '';
94
return;
95
}
96
127
- selectMnemotic(item);
97
+ selectMnemonic(item);
98
}
99
130
- void editTextOfSelectedMnemotic(String text) {
100
+ void editTextOfSelectedMnemonic(String text) {
101
setState(() => selectedItem.changeText(text));
102
selectedItem = null;
103
_seedController.text = '';
104
135
- if (widget.onMnemoticChange != null) {
136
- widget.onMnemoticChange(items);
105
+ if (widget.onMnemonicChange != null) {
106
+ widget.onMnemonicChange(items);
107
}
108
}
109
@@ -143,83 +113,77 @@ class SeedWidgetState extends State<SeedWidget> {
113
selectedItem = null;
114
_seedController.text = '';
115
146
- if (widget.onMnemoticChange != null) {
147
- widget.onMnemoticChange(items);
116
+ if (widget.onMnemonicChange != null) {
117
+ widget.onMnemonicChange(items);
118
}
119
});
120
}
121
152
- void invalidate() {
153
- setState(() => isValid = false);
154
- }
122
+ void invalidate() => setState(() => isValid = false);
123
156
- void validated() {
157
- setState(() => isValid = true);
158
- }
124
+ void validated() => setState(() => isValid = true);
125
160
- void setErrorMessage(String errorMessage) {
161
- setState(() => this.errorMessage = errorMessage);
162
- }
126
+ void setErrorMessage(String errorMessage) =>
127
+ setState(() => this.errorMessage = errorMessage);
128
129
void replaceText(String text) {
130
setState(() => items = []);
166
- mnemoticFromText(text);
131
+ mnemonicFromText(text);
132
}
133
169
- void changeCurrentMnemotic(String text) {
134
+ void changeCurrentMnemonic(String text) {
135
setState(() {
136
final trimmedText = text.trim();
137
final splitted = trimmedText.split(' ');
138
_errorMessage = null;
139
140
if (text == null) {
176
- currentMnemotics = [];
177
- isCurrentMnemoticValid = false;
141
+ currentMnemonics = [];
142
+ isCurrentMnemonicValid = false;
143
return;
144
}
145
181
- currentMnemotics = splitted
182
- .map((text) => MnemoticItem(text: text, dic: widget.words))
183
- .toList();
146
+ currentMnemonics =
147
+ splitted.map((text) => MnemonicItem(text: text)).toList();
148
185
- bool isValid = true;
149
+ var isValid = true;
150
187
- for (final word in currentMnemotics) {
188
- isValid = word.isCorrect();
151
+ for (final word in currentMnemonics) {
152
+ isValid = widget.validator.isValid(word);
153
154
if (!isValid) {
155
break;
156
}
157
}
158
195
- isCurrentMnemoticValid = isValid;
159
+ isCurrentMnemonicValid = isValid;
160
});
161
}
162
199
- void saveCurrentMnemoticToItems() {
163
+ void saveCurrentMnemonicToItems() {
164
setState(() {
165
if (selectedItem != null) {
202
- selectedItem.changeText(currentMnemotics.first.text.trim());
166
+ selectedItem.changeText(currentMnemonics.first.text.trim());
167
selectedItem = null;
168
} else {
205
- items.addAll(currentMnemotics);
169
+ items.addAll(currentMnemonics);
170
}
171
208
- currentMnemotics = [];
172
+ currentMnemonics = [];
173
_seedController.text = '';
174
});
175
}
176
177
void showErrorIfExist() {
178
setState(() => _errorMessage =
215
- !isCurrentMnemoticValid ? S.current.incorrect_seed : null);
179
+ !isCurrentMnemonicValid ? S.current.incorrect_seed : null);
180
}
181
182
bool isSeedValid() {
183
bool isValid;
184
185
for (final item in items) {
222
- isValid = item.isCorrect();
186
+ isValid = widget.validator.isValid(item);
187
188
if (!isValid) {
189
break;
@@ -234,192 +198,207 @@ class SeedWidgetState extends State<SeedWidget> {
198
return Container(
199
child: Column(children: [
200
Flexible(
237
- fit: FlexFit.tight,
238
- flex: 1,
239
- child: Container(
240
- width: double.infinity,
241
- height: double.infinity,
242
- padding: EdgeInsets.all(24),
243
- decoration: BoxDecoration(
244
- borderRadius: BorderRadius.only(
245
- bottomLeft: Radius.circular(24),
246
- bottomRight: Radius.circular(24)
201
+ fit: FlexFit.tight,
202
+ flex: 1,
203
+ child: Container(
204
+ width: double.infinity,
205
+ height: double.infinity,
206
+ padding: EdgeInsets.all(24),
207
+ decoration: BoxDecoration(
208
+ borderRadius: BorderRadius.only(
209
+ bottomLeft: Radius.circular(24),
210
+ bottomRight: Radius.circular(24)),
211
+ color: Theme.of(context).accentTextTheme.title.backgroundColor),
212
+ child: SingleChildScrollView(
213
+ child: Column(
214
+ mainAxisAlignment: MainAxisAlignment.start,
215
+ crossAxisAlignment: CrossAxisAlignment.start,
216
+ children: <Widget>[
217
+ Text(
218
+ S.of(context).restore_active_seed,
219
+ style: TextStyle(
220
+ fontSize: 14,
221
+ color:
222
+ Theme.of(context).primaryTextTheme.caption.color),
223
),
248
- color: Theme.of(context).accentTextTheme.title.backgroundColor
249
- ),
250
- child: SingleChildScrollView(
251
- child: Column(
252
- mainAxisAlignment: MainAxisAlignment.start,
253
- crossAxisAlignment: CrossAxisAlignment.start,
254
- children: <Widget>[
255
- Text(
256
- S.of(context).restore_active_seed,
257
- style: TextStyle(
258
- fontSize: 14,
259
- color: Theme.of(context).primaryTextTheme.caption.color
260
- ),
261
- ),
262
- Padding(
263
- padding: EdgeInsets.only(top: 5),
264
- child: Wrap(
265
- children: items.map((item) {
266
- final isValid = item.isCorrect();
267
- final isSelected = selectedItem == item;
268
-
269
- return InkWell(
270
- onTap: () => onMnemoticTap(item),
271
- child: Container(
272
- decoration: BoxDecoration(
273
- color: isValid ? Colors.transparent : Palette.red),
274
- margin: EdgeInsets.only(right: 7, bottom: 8),
275
- child: Text(
276
- item.toString(),
277
- style: TextStyle(
278
- color: isValid
279
- ? Theme.of(context).primaryTextTheme.title.color
280
- : Theme.of(context).primaryTextTheme.caption.color,
281
- fontSize: 16,
282
- fontWeight:
283
- isSelected ? FontWeight.w900 : FontWeight.w400,
284
- decoration: isSelected
285
- ? TextDecoration.underline
286
- : TextDecoration.none),
287
- )),
288
- );
289
- }).toList(),)
290
- )
291
- ],
292
- ),
224
+ Padding(
225
+ padding: EdgeInsets.only(top: 5),
226
+ child: Wrap(
227
+ children: items.map((item) {
228
+ final isValid = widget.validator.isValid(item);
229
+ final isSelected = selectedItem == item;
230
+
231
+ return InkWell(
232
+ onTap: () => onMnemonicTap(item),
233
+ child: Container(
234
+ decoration: BoxDecoration(
235
+ color: isValid
236
+ ? Colors.transparent
237
+ : Palette.red),
238
+ margin: EdgeInsets.only(right: 7, bottom: 8),
239
+ child: Text(
240
+ item.toString(),
241
+ style: TextStyle(
242
+ color: isValid
243
+ ? Theme.of(context)
244
+ .primaryTextTheme
245
+ .title
246
+ .color
247
+ : Theme.of(context)
248
+ .primaryTextTheme
249
+ .caption
250
+ .color,
251
+ fontSize: 16,
252
+ fontWeight: isSelected
253
+ ? FontWeight.w900
254
+ : FontWeight.w400,
255
+ decoration: isSelected
256
+ ? TextDecoration.underline
257
+ : TextDecoration.none),
258
+ )),
259
+ );
260
+ }).toList(),
261
+ ))
262
+ ],
263
),
264
),
265
+ ),
266
),
267
Flexible(
268
fit: FlexFit.tight,
269
flex: 2,
270
child: Padding(
300
- padding: EdgeInsets.only(left: 24, top: 48, right: 24, bottom: 24),
271
+ padding:
272
+ EdgeInsets.only(left: 24, top: 48, right: 24, bottom: 24),
273
child: Column(
302
- mainAxisAlignment: MainAxisAlignment.start,
303
- crossAxisAlignment: CrossAxisAlignment.center,
304
- children: <Widget>[
305
- Text(
306
- S.of(context).restore_new_seed,
307
- style: TextStyle(
308
- fontSize: 18,
309
- fontWeight: FontWeight.bold,
310
- color: Theme.of(context).primaryTextTheme.title.color
311
- ),
312
- ),
313
- Padding(
314
- padding: EdgeInsets.only(top: 24),
315
- child: TextFormField(
316
- key: _seedTextFieldKey,
317
- onFieldSubmitted: (text) => isCurrentMnemoticValid
318
- ? saveCurrentMnemoticToItems()
319
- : null,
320
- style: TextStyle(
321
- fontSize: 16.0,
322
- color: Theme.of(context).primaryTextTheme.title.color
274
+ mainAxisAlignment: MainAxisAlignment.start,
275
+ crossAxisAlignment: CrossAxisAlignment.center,
276
+ children: <Widget>[
277
+ Text(
278
+ S.of(context).restore_new_seed,
279
+ style: TextStyle(
280
+ fontSize: 18,
281
+ fontWeight: FontWeight.bold,
282
+ color:
283
+ Theme.of(context).primaryTextTheme.title.color),
284
),
324
- controller: _seedController,
325
- textInputAction: TextInputAction.done,
326
- decoration: InputDecoration(
327
- suffixIcon: GestureDetector(
328
- behavior: HitTestBehavior.opaque,
329
- child: ConstrainedBox(
330
- constraints: BoxConstraints(maxWidth: 145),
331
- child: Row(
332
- mainAxisAlignment: MainAxisAlignment.end,
333
- children: <Widget>[
334
- Text(
335
- '${items.length}/${SeedWidgetState.maxLength}',
336
- style: TextStyle(
337
- color: Theme.of(context).primaryTextTheme.caption.color,
338
- fontSize: 14)),
339
- SizedBox(width: 10),
340
- InkWell(
341
- onTap: () async =>
342
- Clipboard.getData('text/plain').then(
285
+ Padding(
286
+ padding: EdgeInsets.only(top: 24),
287
+ child: TextFormField(
288
+ key: _seedTextFieldKey,
289
+ onFieldSubmitted: (text) => isCurrentMnemonicValid
290
+ ? saveCurrentMnemonicToItems()
291
+ : null,
292
+ style: TextStyle(
293
+ fontSize: 16.0,
294
+ color:
295
+ Theme.of(context).primaryTextTheme.title.color),
296
+ controller: _seedController,
297
+ textInputAction: TextInputAction.done,
298
+ decoration: InputDecoration(
299
+ suffixIcon: GestureDetector(
300
+ behavior: HitTestBehavior.opaque,
301
+ child: ConstrainedBox(
302
+ constraints: BoxConstraints(maxWidth: 145),
303
+ child: Row(
304
+ mainAxisAlignment: MainAxisAlignment.end,
305
+ children: <Widget>[
306
+ Text('${items.length}/$maxLength',
307
+ style: TextStyle(
308
+ color: Theme.of(context)
309
+ .primaryTextTheme
310
+ .caption
311
+ .color,
312
+ fontSize: 14)),
313
+ SizedBox(width: 10),
314
+ InkWell(
315
+ onTap: () async =>
316
+ Clipboard.getData('text/plain').then(
317
(clipboard) =>
344
- replaceText(clipboard.text)),
345
- child: Container(
346
- height: 35,
347
- padding: EdgeInsets.all(7),
348
- decoration: BoxDecoration(
349
- color:
350
- Theme.of(context).accentTextTheme.title.backgroundColor,
351
- borderRadius:
352
- BorderRadius.circular(10.0)),
353
- child: Text(
354
- S.of(context).paste,
355
- style: TextStyle(
356
- color: Theme.of(context).primaryTextTheme.title.color
357
- ),
358
- )),
359
- )
360
- ],
318
+ replaceText(clipboard.text)),
319
+ child: Container(
320
+ height: 35,
321
+ padding: EdgeInsets.all(7),
322
+ decoration: BoxDecoration(
323
+ color: Theme.of(context)
324
+ .accentTextTheme
325
+ .title
326
+ .backgroundColor,
327
+ borderRadius:
328
+ BorderRadius.circular(10.0)),
329
+ child: Text(
330
+ S.of(context).paste,
331
+ style: TextStyle(
332
+ color: Theme.of(context)
333
+ .primaryTextTheme
334
+ .title
335
+ .color),
336
+ )),
337
+ )
338
+ ],
339
+ ),
340
+ ),
341
),
362
- ),
363
- ),
364
- hintStyle:
365
- TextStyle(
366
- color: Theme.of(context).primaryTextTheme.caption.color,
367
- fontSize: 16
368
- ),
369
- hintText: S.of(context).restore_from_seed_placeholder,
370
- errorText: _errorMessage,
371
- focusedBorder: UnderlineInputBorder(
372
- borderSide: BorderSide(
373
- color: Theme.of(context).dividerColor, width: 1.0)),
374
- enabledBorder: UnderlineInputBorder(
375
- borderSide: BorderSide(
376
- color: Theme.of(context).dividerColor,
377
- width: 1.0))),
378
- enableInteractiveSelection: false,
379
- ),
380
- )
381
- ]),
382
- )
383
- ),
342
+ hintStyle: TextStyle(
343
+ color: Theme.of(context)
344
+ .primaryTextTheme
345
+ .caption
346
+ .color,
347
+ fontSize: 16),
348
+ hintText:
349
+ S.of(context).restore_from_seed_placeholder,
350
+ errorText: _errorMessage,
351
+ focusedBorder: UnderlineInputBorder(
352
+ borderSide: BorderSide(
353
+ color: Theme.of(context).dividerColor,
354
+ width: 1.0)),
355
+ enabledBorder: UnderlineInputBorder(
356
+ borderSide: BorderSide(
357
+ color: Theme.of(context).dividerColor,
358
+ width: 1.0))),
359
+ enableInteractiveSelection: false,
360
+ ),
361
+ )
362
+ ]),
363
+ )),
364
Padding(
365
padding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
366
child: Row(
367
children: <Widget>[
368
Flexible(
389
- child: Padding(
390
- padding: EdgeInsets.only(right: 8),
391
- child: PrimaryButton(
392
- onPressed: clear,
393
- text: S.of(context).clear,
394
- color: Colors.red,
395
- textColor: Colors.white,
396
- isDisabled: items.isEmpty,
397
- ),
398
- )
399
- ),
369
+ child: Padding(
370
+ padding: EdgeInsets.only(right: 8),
371
+ child: PrimaryButton(
372
+ onPressed: clear,
373
+ text: S.of(context).clear,
374
+ color: Colors.red,
375
+ textColor: Colors.white,
376
+ isDisabled: items.isEmpty,
377
+ ),
378
+ )),
379
Flexible(
380
child: Padding(
381
padding: EdgeInsets.only(left: 8),
382
child: (selectedItem == null && items.length == maxLength)
383
? PrimaryButton(
405
- text: S.of(context).restore_next,
406
- isDisabled: !isSeedValid(),
407
- onPressed: () => widget.onFinish != null
408
- ? widget.onFinish()
409
- : null,
410
- color: Colors.green,
411
- textColor: Colors.white)
384
+ text: S.of(context).restore_next,
385
+ isDisabled: !isSeedValid(),
386
+ onPressed: () => widget.onFinish != null
387
+ ? widget.onFinish()
388
+ : null,
389
+ color: Colors.green,
390
+ textColor: Colors.white)
391
: PrimaryButton(
413
- text: selectedItem != null
414
- ? S.of(context).save
415
- : S.of(context).add_new_word,
416
- onPressed: () => isCurrentMnemoticValid
417
- ? saveCurrentMnemoticToItems()
418
- : null,
419
- onDisabledPressed: () => showErrorIfExist(),
420
- isDisabled: !isCurrentMnemoticValid,
421
- color: Colors.green,
422
- textColor: Colors.white),
392
+ text: selectedItem != null
393
+ ? S.of(context).save
394
+ : S.of(context).add_new_word,
395
+ onPressed: () => isCurrentMnemonicValid
396
+ ? saveCurrentMnemonicToItems()
397
+ : null,
398
+ onDisabledPressed: () => showErrorIfExist(),
399
+ isDisabled: !isCurrentMnemonicValid,
400
+ color: Colors.green,
401
+ textColor: Colors.white),
402
),
403
)
404
],
lib/store/app_store.dart
new
+16
@@ -0,0 +1,16 @@
1
+import 'package:mobx/mobx.dart';
2
+import 'package:cake_wallet/core/wallet_base.dart';
3
+import 'package:cake_wallet/store/authentication_store.dart';
4
+
5
+part 'app_store.g.dart';
6
+
7
+class AppStore = AppStoreBase with _$AppStore;
8
+
9
+abstract class AppStoreBase with Store {
10
+ AppStoreBase({this.authenticationStore});
11
+
12
+ AuthenticationStore authenticationStore;
13
+
14
+ @observable
15
+ WalletBase wallet;
16
+}
lib/store/authentication_store.dart
new
+23
@@ -0,0 +1,23 @@
1
+import 'package:mobx/mobx.dart';
2
+
3
+part 'authentication_store.g.dart';
4
+
5
+class AuthenticationStore = AuthenticationStoreBase with _$AuthenticationStore;
6
+
7
+enum AuthenticationState { uninitialized, installed, allowed, denied }
8
+
9
+abstract class AuthenticationStoreBase with Store {
10
+ AuthenticationStoreBase() : state = AuthenticationState.uninitialized;
11
+
12
+ @observable
13
+ AuthenticationState state;
14
+
15
+ @action
16
+ void installed() => state = AuthenticationState.installed;
17
+
18
+ @action
19
+ void allowed() => state = AuthenticationState.allowed;
20
+
21
+ @action
22
+ void denied() => state = AuthenticationState.denied;
23
+}
lib/store/wallet_list_store.dart
new
+10
@@ -0,0 +1,10 @@
1
+import 'package:mobx/mobx.dart';
2
+
3
+part 'wallet_list_store.g.dart';
4
+
5
+class WalletListStore = WalletListStoreBase with _$WalletListStore;
6
+
7
+abstract class WalletListStoreBase with Store {
8
+ @observable
9
+ Object state;
10
+}
\ No newline at end of file
lib/themes.dart
+5
-3
@@ -44,9 +44,10 @@ class Themes {
44
),
45
display4: TextStyle(
46
color: Palette.oceanBlue // QR code
47
- )
47
+ ),
48
+// headline1: TextStyle(color: Palette.nightBlue)
49
),
49
- dividerColor: Palette.periwinkle,
50
+ dividerColor: Palette.eee,
51
accentTextTheme: TextTheme(
52
title: TextStyle(
53
color: Palette.darkLavender, // top panel
@@ -112,7 +113,8 @@ class Themes {
113
),
114
display4: TextStyle(
115
color: PaletteDark.gray // QR code
115
- )
116
+ ),
117
+// headline5: TextStyle(color: PaletteDark.gray)
118
),
119
dividerColor: PaletteDark.distantBlue,
120
accentTextTheme: TextTheme(
lib/utils/list_item.dart
new
+3
@@ -0,0 +1,3 @@
1
+abstract class ListItem {
2
+ const ListItem();
3
+}
\ No newline at end of file
lib/utils/list_section.dart
new
+5
@@ -0,0 +1,5 @@
1
+class ListSection<Item> {
2
+ const ListSection({this.items});
3
+
4
+ final List<Item> items;
5
+}
\ No newline at end of file
lib/view_model/address_list/account_list_header.dart
new
+3
@@ -0,0 +1,3 @@
1
+import 'package:cake_wallet/utils/list_item.dart';
2
+
3
+class AccountListHeader extends ListItem {}
\ No newline at end of file
lib/view_model/address_list/address_edit_or_create_view_model.dart
new
+93
@@ -0,0 +1,93 @@
1
+import 'package:flutter/foundation.dart';
2
+import 'package:mobx/mobx.dart';
3
+import 'package:cake_wallet/core/wallet_base.dart';
4
+import 'package:cake_wallet/bitcoin/bitcoin_wallet.dart';
5
+import 'package:cake_wallet/monero/monero_wallet.dart';
6
+
7
+part 'address_edit_or_create_view_model.g.dart';
8
+
9
+class AddressEditOrCreateViewModel = AddressEditOrCreateViewModelBase
10
+ with _$AddressEditOrCreateViewModel;
11
+
12
+abstract class AddressEditOrCreateState {}
13
+
14
+class AddressEditOrCreateStateInitial extends AddressEditOrCreateState {}
15
+
16
+class AddressIsSaving extends AddressEditOrCreateState {}
17
+
18
+class AddressSavedSuccessfully extends AddressEditOrCreateState {}
19
+
20
+class AddressEditOrCreateStateFailure extends AddressEditOrCreateState {
21
+ AddressEditOrCreateStateFailure({this.error});
22
+
23
+ String error;
24
+}
25
+
26
+abstract class AddressEditOrCreateViewModelBase with Store {
27
+ AddressEditOrCreateViewModelBase({@required WalletBase wallet, dynamic item})
28
+ : isEdit = item != null,
29
+ state = AddressEditOrCreateStateInitial(),
30
+ label = item?.name as String,
31
+ _item = item,
32
+ _wallet = wallet;
33
+
34
+ dynamic _item;
35
+
36
+ @observable
37
+ AddressEditOrCreateState state;
38
+
39
+ @observable
40
+ String label;
41
+
42
+ bool isEdit;
43
+
44
+ final WalletBase _wallet;
45
+
46
+ Future<void> save() async {
47
+ final wallet = _wallet;
48
+
49
+ try {
50
+ state = AddressIsSaving();
51
+
52
+ if (isEdit) {
53
+ await _update();
54
+ } else {
55
+ await _createNew();
56
+ }
57
+
58
+ state = AddressSavedSuccessfully();
59
+ } catch (e) {
60
+ state = AddressEditOrCreateStateFailure(error: e.toString());
61
+ }
62
+ }
63
+
64
+ Future<void> _createNew() async {
65
+ final wallet = _wallet;
66
+
67
+ if (wallet is BitcoinWallet) {
68
+ await wallet.generateNewAddress(label: label);
69
+ }
70
+
71
+ if (wallet is MoneroWallet) {
72
+ await wallet.subaddressList
73
+ .addSubaddress(accountIndex: wallet.account.id, label: label);
74
+ await wallet.save();
75
+ }
76
+ }
77
+
78
+ Future<void> _update() async {
79
+ final wallet = _wallet;
80
+
81
+ if (wallet is BitcoinWallet) {
82
+ await wallet.updateAddress(_item.address as String, label: label);
83
+ }
84
+
85
+ if (wallet is MoneroWallet) {
86
+ await wallet.subaddressList.setLabelSubaddress(
87
+ accountIndex: wallet.account.id,
88
+ addressIndex: _item.id as int,
89
+ label: label);
90
+ await wallet.save();
91
+ }
92
+ }
93
+}
lib/view_model/address_list/address_list_header.dart
new
+3
@@ -0,0 +1,3 @@
1
+import 'package:cake_wallet/utils/list_item.dart';
2
+
3
+class AddressListHeader extends ListItem {}
\ No newline at end of file
lib/view_model/address_list/address_list_item.dart
new
+14
@@ -0,0 +1,14 @@
1
+import 'package:flutter/foundation.dart';
2
+import 'package:cake_wallet/utils/list_item.dart';
3
+
4
+class AddressListItem extends ListItem {
5
+ const AddressListItem({@required this.address, this.name, this.id})
6
+ : super();
7
+
8
+ final int id;
9
+ final String address;
10
+ final String name;
11
+
12
+ @override
13
+ String toString() => name ?? address;
14
+}
\ No newline at end of file
lib/view_model/address_list/address_list_view_model.dart
new
+135
@@ -0,0 +1,135 @@
1
+import 'package:flutter/foundation.dart';
2
+import 'package:mobx/mobx.dart';
3
+import 'package:cake_wallet/bitcoin/bitcoin_wallet.dart';
4
+import 'package:cake_wallet/monero/monero_wallet.dart';
5
+import 'package:cake_wallet/core/wallet_base.dart';
6
+import 'package:cake_wallet/utils/list_item.dart';
7
+import 'package:cake_wallet/view_model/address_list/account_list_header.dart';
8
+import 'package:cake_wallet/view_model/address_list/address_list_header.dart';
9
+import 'package:cake_wallet/view_model/address_list/address_list_item.dart';
10
+
11
+part 'address_list_view_model.g.dart';
12
+
13
+class AddressListViewModel = AddressListViewModelBase
14
+ with _$AddressListViewModel;
15
+
16
+abstract class PaymentURI {
17
+ PaymentURI({this.amount, this.address});
18
+
19
+ final String amount;
20
+ final String address;
21
+}
22
+
23
+class MoneroURI extends PaymentURI {
24
+ MoneroURI({String amount, String address})
25
+ : super(amount: amount, address: address);
26
+
27
+ @override
28
+ String toString() {
29
+ var base = 'monero:' + address;
30
+
31
+ if (amount?.isNotEmpty ?? false) {
32
+ base += '?tx_amount=$amount';
33
+ }
34
+
35
+ return base;
36
+ }
37
+}
38
+
39
+class BitcoinURI extends PaymentURI {
40
+ BitcoinURI({String amount, String address})
41
+ : super(amount: amount, address: address);
42
+
43
+ @override
44
+ String toString() {
45
+ var base = 'bitcoin:' + address;
46
+
47
+ if (amount?.isNotEmpty ?? false) {
48
+ base += '?amount=$amount';
49
+ }
50
+
51
+ return base;
52
+ }
53
+}
54
+
55
+abstract class AddressListViewModelBase with Store {
56
+ AddressListViewModelBase({@required WalletBase wallet}) {
57
+ hasAccounts = _wallet is MoneroWallet;
58
+ _wallet = wallet;
59
+ _init();
60
+ }
61
+
62
+ @observable
63
+ String amount;
64
+
65
+ @computed
66
+ AddressListItem get address => AddressListItem(address: _wallet.address);
67
+
68
+ @computed
69
+ PaymentURI get uri {
70
+ if (_wallet is MoneroWallet) {
71
+ return MoneroURI(amount: amount, address: address.address);
72
+ }
73
+
74
+ if (_wallet is BitcoinWallet) {
75
+ return BitcoinURI(amount: amount, address: address.address);
76
+ }
77
+
78
+ return null;
79
+ }
80
+
81
+ @computed
82
+ ObservableList<ListItem> get items =>
83
+ ObservableList<ListItem>()..addAll(_baseItems)..addAll(addressList);
84
+
85
+ @computed
86
+ ObservableList<ListItem> get addressList {
87
+ final wallet = _wallet;
88
+ final addressList = ObservableList<ListItem>();
89
+
90
+ if (wallet is MoneroWallet) {
91
+ addressList.addAll(wallet.subaddressList.subaddresses.map((subaddress) =>
92
+ AddressListItem(
93
+ id: subaddress.id,
94
+ name: subaddress.label,
95
+ address: subaddress.address)));
96
+ }
97
+
98
+ if (wallet is BitcoinWallet) {
99
+ final bitcoinAddresses = wallet.addresses.map(
100
+ (addr) => AddressListItem(name: addr.label, address: addr.address));
101
+ addressList.addAll(bitcoinAddresses);
102
+ }
103
+
104
+ return addressList;
105
+ }
106
+
107
+ set address(AddressListItem address) => _wallet.address = address.address;
108
+
109
+ bool hasAccounts;
110
+
111
+ WalletBase _wallet;
112
+
113
+ List<ListItem> _baseItems;
114
+
115
+ @computed
116
+ String get accountLabel {
117
+ final wallet = _wallet;
118
+
119
+ if (wallet is MoneroWallet) {
120
+ return wallet.account.label;
121
+ }
122
+
123
+ return null;
124
+ }
125
+
126
+ void _init() {
127
+ _baseItems = [];
128
+
129
+ if (_wallet is MoneroWallet) {
130
+ _baseItems.add(AccountListHeader());
131
+ }
132
+
133
+ _baseItems.add(AddressListHeader());
134
+ }
135
+}
lib/view_model/auth_state.dart
new
+20
@@ -0,0 +1,20 @@
1
+abstract class AuthState {}
2
+
3
+class AuthenticationStateInitial extends AuthState {}
4
+
5
+class AuthenticationInProgress extends AuthState {}
6
+
7
+class AuthenticatedSuccessfully extends AuthState {}
8
+
9
+class AuthenticationFailure extends AuthState {
10
+ AuthenticationFailure({this.error});
11
+
12
+ final String error;
13
+}
14
+
15
+class AuthenticationBanned extends AuthState {
16
+ AuthenticationBanned({this.error});
17
+
18
+ final String error;
19
+}
20
+
lib/view_model/auth_view_model.dart
new
+97
@@ -0,0 +1,97 @@
1
+import 'dart:async';
2
+import 'package:flutter/foundation.dart';
3
+import 'package:shared_preferences/shared_preferences.dart';
4
+import 'package:mobx/mobx.dart';
5
+import 'package:cake_wallet/view_model/auth_state.dart';
6
+import 'package:cake_wallet/core/auth_service.dart';
7
+import 'package:cake_wallet/generated/i18n.dart';
8
+
9
+part 'auth_view_model.g.dart';
10
+
11
+class AuthViewModel = AuthViewModelBase with _$AuthViewModel;
12
+
13
+abstract class AuthViewModelBase with Store {
14
+ AuthViewModelBase(
15
+ {@required this.authService, @required this.sharedPreferences}) {
16
+ state = AuthenticationStateInitial();
17
+ _failureCounter = 0;
18
+ }
19
+
20
+ static const maxFailedLogins = 3;
21
+ static const banTimeout = 180; // 3 mins
22
+ final banTimeoutKey = S.current.auth_store_ban_timeout;
23
+
24
+ final AuthService authService;
25
+ final SharedPreferences sharedPreferences;
26
+
27
+ @observable
28
+ AuthState state;
29
+
30
+ @observable
31
+ int _failureCounter;
32
+
33
+ @action
34
+ Future<void> auth({String password}) async {
35
+ state = AuthenticationStateInitial();
36
+ final _banDuration = banDuration();
37
+
38
+ if (_banDuration != null) {
39
+ state = AuthenticationBanned(
40
+ error: S.current.auth_store_banned_for +
41
+ '${_banDuration.inMinutes}' +
42
+ S.current.auth_store_banned_minutes);
43
+ return;
44
+ }
45
+
46
+ state = AuthenticationInProgress();
47
+ final isAuth = await authService.authenticate(password);
48
+
49
+ if (isAuth) {
50
+ state = AuthenticatedSuccessfully();
51
+ _failureCounter = 0;
52
+ } else {
53
+ _failureCounter += 1;
54
+
55
+ if (_failureCounter >= maxFailedLogins) {
56
+ final banDuration = await ban();
57
+ state = AuthenticationBanned(
58
+ error: S.current.auth_store_banned_for +
59
+ '${banDuration.inMinutes}' +
60
+ S.current.auth_store_banned_minutes);
61
+ return;
62
+ }
63
+
64
+ state =
65
+ AuthenticationFailure(error: S.current.auth_store_incorrect_password);
66
+ }
67
+ }
68
+
69
+ Duration banDuration() {
70
+ final unbanTimestamp = sharedPreferences.getInt(banTimeoutKey);
71
+
72
+ if (unbanTimestamp == null) {
73
+ return null;
74
+ }
75
+
76
+ final unbanTime = DateTime.fromMillisecondsSinceEpoch(unbanTimestamp);
77
+ final now = DateTime.now();
78
+
79
+ if (now.isAfter(unbanTime)) {
80
+ return null;
81
+ }
82
+
83
+ return Duration(milliseconds: unbanTimestamp - now.millisecondsSinceEpoch);
84
+ }
85
+
86
+ Future<Duration> ban() async {
87
+ final multiplier = _failureCounter - maxFailedLogins + 1;
88
+ final timeout = (multiplier * banTimeout) * 1000;
89
+ final unbanTimestamp = DateTime.now().millisecondsSinceEpoch + timeout;
90
+ await sharedPreferences.setInt(banTimeoutKey, unbanTimestamp);
91
+
92
+ return Duration(milliseconds: timeout);
93
+ }
94
+
95
+ @action
96
+ void biometricAuth() => state = AuthenticatedSuccessfully();
97
+}
lib/view_model/dashboard_view_model.dart
new
+68
@@ -0,0 +1,68 @@
1
+import 'package:cake_wallet/bitcoin/bitcoin_transaction_info.dart';
2
+import 'package:cake_wallet/src/domain/common/transaction_direction.dart';
3
+import 'package:cake_wallet/src/domain/common/transaction_info.dart';
4
+import 'package:cake_wallet/src/stores/action_list/transaction_list_item.dart';
5
+import 'package:mobx/mobx.dart';
6
+import 'package:cake_wallet/core/wallet_base.dart';
7
+import 'package:cake_wallet/src/domain/common/sync_status.dart';
8
+import 'package:cake_wallet/src/domain/common/wallet_type.dart';
9
+import 'package:cake_wallet/store/app_store.dart';
10
+
11
+part 'dashboard_view_model.g.dart';
12
+
13
+class DashboardViewModel = DashboardViewModelBase with _$DashboardViewModel;
14
+
15
+class WalletBalace {
16
+ WalletBalace({this.unlockedBalance, this.totalBalance});
17
+
18
+ final String unlockedBalance;
19
+ final String totalBalance;
20
+}
21
+
22
+abstract class DashboardViewModelBase with Store {
23
+ DashboardViewModelBase({this.appStore}) {
24
+ name = appStore.wallet?.name;
25
+ balance = WalletBalace(unlockedBalance: '0.001', totalBalance: '0.005');
26
+ status = SyncedSyncStatus();
27
+ type = WalletType.bitcoin;
28
+ wallet ??= appStore.wallet;
29
+ _reaction = reaction((_) => appStore.wallet, _onWalletChange);
30
+ transactions = ObservableList.of(wallet.transactionHistory.transactions
31
+ .map((transaction) => TransactionListItem(transaction: transaction)));
32
+ }
33
+
34
+ @observable
35
+ WalletType type;
36
+
37
+ @observable
38
+ String name;
39
+
40
+ @computed
41
+ String get address => wallet.address;
42
+
43
+ @observable
44
+ WalletBalace balance;
45
+
46
+ @observable
47
+ SyncStatus status;
48
+
49
+ @observable
50
+ ObservableList<Object> transactions;
51
+
52
+ @observable
53
+ String subname;
54
+
55
+ WalletBase wallet;
56
+
57
+ AppStore appStore;
58
+
59
+ ReactionDisposer _reaction;
60
+
61
+ void _onWalletChange(WalletBase wallet) {
62
+ name = wallet.name;
63
+ transactions.clear();
64
+ transactions.addAll(wallet.transactionHistory.transactions
65
+ .map((transaction) => TransactionListItem(transaction: transaction)));
66
+ balance = WalletBalace(unlockedBalance: '0.001', totalBalance: '0.005');
67
+ }
68
+}
lib/view_model/wallet_creation_state.dart
new
+15
@@ -0,0 +1,15 @@
1
+import 'package:flutter/foundation.dart';
2
+
3
+abstract class WalletCreationState {}
4
+
5
+class InitialWalletCreationState extends WalletCreationState {}
6
+
7
+class WalletCreating extends WalletCreationState {}
8
+
9
+class WalletCreatedSuccessfully extends WalletCreationState {}
10
+
11
+class WalletCreationFailure extends WalletCreationState {
12
+ WalletCreationFailure({@required this.error});
13
+
14
+ final String error;
15
+}
lib/view_model/wallet_creation_vm.dart
new
+40
@@ -0,0 +1,40 @@
1
+import 'package:flutter/foundation.dart';
2
+import 'package:mobx/mobx.dart';
3
+import 'package:cake_wallet/core/wallet_credentials.dart';
4
+import 'package:cake_wallet/src/domain/common/wallet_type.dart';
5
+import 'package:cake_wallet/view_model/wallet_creation_state.dart';
6
+
7
+part 'wallet_creation_vm.g.dart';
8
+
9
+class WalletCreationVM = WalletCreationVMBase with _$WalletCreationVM;
10
+
11
+abstract class WalletCreationVMBase with Store {
12
+ WalletCreationVMBase({@required this.type}) {
13
+ state = InitialWalletCreationState();
14
+ name = '';
15
+ }
16
+
17
+ @observable
18
+ String name;
19
+
20
+ @observable
21
+ WalletCreationState state;
22
+
23
+ WalletType type;
24
+
25
+ Future<void> create({dynamic options}) async {
26
+ try {
27
+ state = WalletCreating();
28
+ await process(getCredentials(options));
29
+ state = WalletCreatedSuccessfully();
30
+ } catch (e) {
31
+ state = WalletCreationFailure(error: e.toString());
32
+ }
33
+ }
34
+
35
+ WalletCredentials getCredentials(dynamic options) =>
36
+ throw UnimplementedError();
37
+
38
+ Future<void> process(WalletCredentials credentials) =>
39
+ throw UnimplementedError();
40
+}
lib/view_model/wallet_new_vm.dart
new
+42
@@ -0,0 +1,42 @@
1
+import 'package:flutter/foundation.dart';
2
+import 'package:mobx/mobx.dart';
3
+import 'package:cake_wallet/monero/monero_wallet_service.dart';
4
+import 'package:cake_wallet/bitcoin/bitcoin_wallet_creation_credentials.dart';
5
+import 'package:cake_wallet/core/wallet_creation_service.dart';
6
+import 'package:cake_wallet/core/wallet_credentials.dart';
7
+import 'package:cake_wallet/src/domain/common/wallet_type.dart';
8
+import 'package:cake_wallet/view_model/wallet_creation_vm.dart';
9
+
10
+part 'wallet_new_vm.g.dart';
11
+
12
+class WalletNewVM = WalletNewVMBase with _$WalletNewVM;
13
+
14
+abstract class WalletNewVMBase extends WalletCreationVM with Store {
15
+ WalletNewVMBase(this._walletCreationService, {@required WalletType type})
16
+ : selectedMnemonicLanguage = '',
17
+ super(type: type);
18
+
19
+ @observable
20
+ String selectedMnemonicLanguage;
21
+
22
+ bool get hasLanguageSelector => type == WalletType.monero;
23
+
24
+ final WalletCreationService _walletCreationService;
25
+
26
+ @override
27
+ WalletCredentials getCredentials(dynamic options) {
28
+ switch (type) {
29
+ case WalletType.monero:
30
+ return MoneroNewWalletCredentials(
31
+ name: name, language: options as String);
32
+ case WalletType.bitcoin:
33
+ return BitcoinNewWalletCredentials(name: name);
34
+ default:
35
+ return null;
36
+ }
37
+ }
38
+
39
+ @override
40
+ Future<void> process(WalletCredentials credentials) async =>
41
+ _walletCreationService.create(credentials);
42
+}
lib/view_model/wallet_restoration_from_seed_vm.dart
new
+52
@@ -0,0 +1,52 @@
1
+import 'package:flutter/foundation.dart';
2
+import 'package:mobx/mobx.dart';
3
+import 'package:cake_wallet/monero/monero_wallet_service.dart';
4
+import 'package:cake_wallet/bitcoin/bitcoin_wallet_creation_credentials.dart';
5
+import 'package:cake_wallet/core/generate_wallet_password.dart';
6
+import 'package:cake_wallet/core/wallet_creation_service.dart';
7
+import 'package:cake_wallet/core/wallet_credentials.dart';
8
+import 'package:cake_wallet/src/domain/common/wallet_type.dart';
9
+import 'package:cake_wallet/view_model/wallet_creation_vm.dart';
10
+
11
+part 'wallet_restoration_from_seed_vm.g.dart';
12
+
13
+class WalletRestorationFromSeedVM = WalletRestorationFromSeedVMBase
14
+ with _$WalletRestorationFromSeedVM;
15
+
16
+abstract class WalletRestorationFromSeedVMBase extends WalletCreationVM
17
+ with Store {
18
+ WalletRestorationFromSeedVMBase(this._walletCreationService,
19
+ {@required WalletType type, @required this.language, this.seed})
20
+ : super(type: type);
21
+
22
+ @observable
23
+ String seed;
24
+
25
+ @observable
26
+ int height;
27
+
28
+ bool get hasRestorationHeight => type == WalletType.monero;
29
+
30
+ final String language;
31
+ final WalletCreationService _walletCreationService;
32
+
33
+ @override
34
+ WalletCredentials getCredentials(dynamic options) {
35
+ final password = generateWalletPassword(type);
36
+
37
+ switch (type) {
38
+ case WalletType.monero:
39
+ return MoneroRestoreWalletFromSeedCredentials(
40
+ name: name, height: height, mnemonic: seed, password: password);
41
+ case WalletType.bitcoin:
42
+ return BitcoinRestoreWalletFromSeedCredentials(
43
+ name: name, mnemonic: seed, password: password);
44
+ default:
45
+ return null;
46
+ }
47
+ }
48
+
49
+ @override
50
+ Future<void> process(WalletCredentials credentials) async =>
51
+ _walletCreationService.restoreFromSeed(credentials);
52
+}
pubspec.lock
+7
@@ -371,6 +371,13 @@ packages:
371
url: "https://pub.dartlang.org"
372
source: hosted
373
version: "0.1.19"
374
+ get_it:
375
+ dependency: "direct main"
376
+ description:
377
+ name: get_it
378
+ url: "https://pub.dartlang.org"
379
+ source: hosted
380
+ version: "4.0.2"
381
glob:
382
dependency: transitive
383
description:
pubspec.yaml
+1
@@ -58,6 +58,7 @@ dependencies:
58
password: ^1.0.0
59
basic_utils: ^1.0.8
60
bitcoin_flutter: ^2.0.0
61
+ get_it: ^4.0.2
62
63
dev_dependencies:
64
flutter_test: