Merge pull request #2 from cake-tech/basic-refactor

Fixed issues for linter and analyzer
This commit is contained in:
M 2020-01-08 15:14:58 +02:00
commit 9e00a6ee2d
168 changed files with 2274 additions and 1971 deletions

View file

@ -5,17 +5,14 @@ analyzer:
implicit-casts: false implicit-casts: false
implicit-dynamic: false implicit-dynamic: false
exclude: exclude:
- **/*.yaml - "build/**"
- build/** - "**/*.g.dart"
- **/*.g.dart - "lib/generated/*.dart"
- lib/generated/*.dart
linter: linter:
rules: rules:
- always_declare_return_types - always_declare_return_types
- always_specify_types
- annotate_overrides - annotate_overrides
- avoid_as
- avoid_empty_else - avoid_empty_else
- avoid_init_to_null - avoid_init_to_null
- avoid_return_types_on_setters - avoid_return_types_on_setters
@ -48,17 +45,13 @@ linter:
- prefer_final_fields - prefer_final_fields
- prefer_final_locals - prefer_final_locals
- prefer_is_not_empty - prefer_is_not_empty
- public_member_api_docs
- slash_for_doc_comments - slash_for_doc_comments
- sort_constructors_first - sort_constructors_first
- sort_unnamed_constructors_first - sort_unnamed_constructors_first
- super_goes_last
- test_types_in_equals - test_types_in_equals
- throw_in_finally - throw_in_finally
- type_annotate_public_apis
- type_init_formals - type_init_formals
- unawaited_futures - unawaited_futures
- unnecessary_brace_in_string_interp
- unnecessary_getters_setters - unnecessary_getters_setters
- unrelated_type_equality_checks - unrelated_type_equality_checks
- valid_regexps - valid_regexps

View file

@ -26,7 +26,7 @@ final accountSetLabelNative = moneroApi
.lookup<NativeFunction<account_set_label>>('account_set_label_row') .lookup<NativeFunction<account_set_label>>('account_set_label_row')
.asFunction<AccountSetLabel>(); .asFunction<AccountSetLabel>();
refreshAccounts() => accountRefreshNative(); void refreshAccounts() => accountRefreshNative();
List<AccountRow> getAllAccount() { List<AccountRow> getAllAccount() {
final size = accountSizeNative(); final size = accountSizeNative();
@ -38,24 +38,29 @@ List<AccountRow> getAllAccount() {
.toList(); .toList();
} }
addAccountSync({String label}) { void addAccountSync({String label}) {
final labelPointer = Utf8.toUtf8(label); final labelPointer = Utf8.toUtf8(label);
accountAddNewNative(labelPointer); accountAddNewNative(labelPointer);
free(labelPointer); free(labelPointer);
} }
setLabelForAccountSync({int accountIndex, String label}) { void setLabelForAccountSync({int accountIndex, String label}) {
final labelPointer = Utf8.toUtf8(label); final labelPointer = Utf8.toUtf8(label);
accountSetLabelNative(accountIndex, labelPointer); accountSetLabelNative(accountIndex, labelPointer);
free(labelPointer); free(labelPointer);
} }
_addAccount(String label) => addAccountSync(label: label); void _addAccount(String label) => addAccountSync(label: label);
_setLabelForAccount(Map args) => setLabelForAccountSync( void _setLabelForAccount(Map<String, dynamic> args) {
label: args['label'], accountIndex: args['accountIndex']); final label = args['label'] as String;
final accountIndex = args['accountIndex'] as int;
Future addAccount({String label}) async => compute(_addAccount, label); setLabelForAccountSync(label: label, accountIndex: accountIndex);
}
Future setLabelForAccount({int accountIndex, String label}) async => compute( Future<void> addAccount({String label}) async => compute(_addAccount, label);
_setLabelForAccount, {'accountIndex': accountIndex, 'label': label});
Future<void> setLabelForAccount({int accountIndex, String label}) async =>
compute(
_setLabelForAccount, {'accountIndex': accountIndex, 'label': label});

View file

@ -1,5 +1,5 @@
class ConnectionToNodeException implements Exception { class ConnectionToNodeException implements Exception {
final String message;
ConnectionToNodeException({this.message}); ConnectionToNodeException({this.message});
final String message;
} }

View file

@ -1,8 +1,8 @@
class CreationTransactionException implements Exception { class CreationTransactionException implements Exception {
final String message;
CreationTransactionException({this.message}); CreationTransactionException({this.message});
final String message;
@override @override
String toString() => message; String toString() => message;
} }

View file

@ -1,5 +1,5 @@
class SetupWalletException implements Exception { class SetupWalletException implements Exception {
final String message;
SetupWalletException({this.message}); SetupWalletException({this.message});
final String message;
} }

View file

@ -1,5 +1,5 @@
class WalletCreationException implements Exception { class WalletCreationException implements Exception {
final String message;
WalletCreationException({this.message}); WalletCreationException({this.message});
final String message;
} }

View file

@ -1,5 +1,5 @@
class WalletRestoreFromKeysException implements Exception { class WalletRestoreFromKeysException implements Exception {
final String message;
WalletRestoreFromKeysException({this.message}); WalletRestoreFromKeysException({this.message});
final String message;
} }

View file

@ -1,5 +1,5 @@
class WalletRestoreFromSeedException implements Exception { class WalletRestoreFromSeedException implements Exception {
final String message;
WalletRestoreFromSeedException({this.message}); WalletRestoreFromSeedException({this.message});
final String message;
} }

View file

@ -14,10 +14,10 @@ class PendingTransactionRaw extends Struct {
} }
class PendingTransactionDescription { class PendingTransactionDescription {
PendingTransactionDescription({this.amount, this.fee, this.hash, this.pointerAddress});
final int amount; final int amount;
final int fee; final int fee;
final String hash; final String hash;
final int pointerAddress; final int pointerAddress;
PendingTransactionDescription({this.amount, this.fee, this.hash, this.pointerAddress});
} }

View file

@ -26,7 +26,7 @@ final subaddrressSetLabelNative = moneroApi
.lookup<NativeFunction<subaddress_set_label>>('subaddress_set_label') .lookup<NativeFunction<subaddress_set_label>>('subaddress_set_label')
.asFunction<SubaddressSetLabel>(); .asFunction<SubaddressSetLabel>();
refreshSubaddresses({int accountIndex}) => void refreshSubaddresses({int accountIndex}) =>
subaddressRefreshNative(accountIndex); subaddressRefreshNative(accountIndex);
List<SubaddressRow> getAllSubaddresses() { List<SubaddressRow> getAllSubaddresses() {
@ -40,25 +40,35 @@ List<SubaddressRow> getAllSubaddresses() {
.toList(); .toList();
} }
addSubaddressSync({int accountIndex, String label}) { void addSubaddressSync({int accountIndex, String label}) {
final labelPointer = Utf8.toUtf8(label); final labelPointer = Utf8.toUtf8(label);
subaddrressAddNewNative(accountIndex, labelPointer); subaddrressAddNewNative(accountIndex, labelPointer);
free(labelPointer); free(labelPointer);
} }
setLabelForSubaddressSync({int accountIndex, int addressIndex, String label}) { void setLabelForSubaddressSync(
{int accountIndex, int addressIndex, String label}) {
final labelPointer = Utf8.toUtf8(label); final labelPointer = Utf8.toUtf8(label);
subaddrressSetLabelNative(accountIndex, addressIndex, labelPointer); subaddrressSetLabelNative(accountIndex, addressIndex, labelPointer);
free(labelPointer); free(labelPointer);
} }
_addSubaddress(Map args) => void _addSubaddress(Map<String, dynamic> args) {
addSubaddressSync(accountIndex: args['accountIndex'], label: args['label']); final label = args['label'] as String;
final accountIndex = args['accountIndex'] as int;
_setLabelForSubaddress(Map args) => setLabelForSubaddressSync( addSubaddressSync(accountIndex: accountIndex, label: label);
accountIndex: args['accountIndex'], }
addressIndex: args['addressIndex'],
label: args['label']); void _setLabelForSubaddress(Map<String, dynamic> args) {
final label = args['label'] as String;
final accountIndex = args['accountIndex'] as int;
final addressIndex = args['addressIndex'] as int;
setLabelForSubaddressSync(
accountIndex: accountIndex, addressIndex: addressIndex, label: label);
}
Future addSubaddress({int accountIndex, String label}) async => Future addSubaddress({int accountIndex, String label}) async =>
compute(_addSubaddress, {'accountIndex': accountIndex, 'label': label}); compute(_addSubaddress, {'accountIndex': accountIndex, 'label': label});

View file

@ -29,7 +29,7 @@ final transactionCommitNative = moneroApi
.lookup<NativeFunction<transaction_commit>>('transaction_commit') .lookup<NativeFunction<transaction_commit>>('transaction_commit')
.asFunction<TransactionCommit>(); .asFunction<TransactionCommit>();
refreshTransactions() => transactionsRefreshNative(); void refreshTransactions() => transactionsRefreshNative();
int countOfTransactions() => transactionsCountNative(); int countOfTransactions() => transactionsCountNative();
@ -84,10 +84,10 @@ PendingTransactionDescription createTransactionSync(
pointerAddress: pendingTransactionRawPointer.address); pointerAddress: pendingTransactionRawPointer.address);
} }
commitTransactionFromPointerAddress({int address}) => commitTransaction( void commitTransactionFromPointerAddress({int address}) => commitTransaction(
transactionPointer: Pointer<PendingTransactionRaw>.fromAddress(address)); transactionPointer: Pointer<PendingTransactionRaw>.fromAddress(address));
commitTransaction({Pointer<PendingTransactionRaw> transactionPointer}) { void commitTransaction({Pointer<PendingTransactionRaw> transactionPointer}) {
final errorMessagePointer = allocate<Utf8Box>(); final errorMessagePointer = allocate<Utf8Box>();
final isCommited = final isCommited =
transactionCommitNative(transactionPointer, errorMessagePointer) != 0; transactionCommitNative(transactionPointer, errorMessagePointer) != 0;
@ -99,13 +99,20 @@ commitTransaction({Pointer<PendingTransactionRaw> transactionPointer}) {
} }
} }
PendingTransactionDescription _createTransactionSync(Map args) => PendingTransactionDescription _createTransactionSync(Map args) {
createTransactionSync( final address = args['address'] as String;
address: args['address'], final paymentId = args['paymentId'] as String;
paymentId: args['paymentId'], final amount = args['amount'] as String;
amount: args['amount'], final priorityRaw = args['priorityRaw'] as int;
priorityRaw: args['priorityRaw'], final accountIndex = args['accountIndex'] as int;
accountIndex: args['accountIndex']);
return createTransactionSync(
address: address,
paymentId: paymentId,
amount: amount,
priorityRaw: priorityRaw,
accountIndex: accountIndex);
}
Future<PendingTransactionDescription> createTransaction( Future<PendingTransactionDescription> createTransaction(
{String address, {String address,

View file

@ -179,23 +179,23 @@ bool setupNodeSync(
return isSetupNode; return isSetupNode;
} }
startRefreshSync() => startRefreshNative(); void startRefreshSync() => startRefreshNative();
Future<bool> connectToNode() async => connecToNodeNative() != 0; Future<bool> connectToNode() async => connecToNodeNative() != 0;
setRefreshFromBlockHeight({int height}) => void setRefreshFromBlockHeight({int height}) =>
setRefreshFromBlockHeightNative(height); setRefreshFromBlockHeightNative(height);
setRecoveringFromSeed({bool isRecovery}) => void setRecoveringFromSeed({bool isRecovery}) =>
setRecoveringFromSeedNative(_boolToInt(isRecovery)); setRecoveringFromSeedNative(_boolToInt(isRecovery));
storeSync() { void storeSync() {
final pathPointer = Utf8.toUtf8(''); final pathPointer = Utf8.toUtf8('');
storeNative(pathPointer); storeNative(pathPointer);
free(pathPointer); free(pathPointer);
} }
closeCurrentWallet() => closeCurrentWalletNative(); void closeCurrentWallet() => closeCurrentWalletNative();
String getSecretViewKey() => String getSecretViewKey() =>
convertUTF8ToString(pointer: getSecretViewKeyNative()); convertUTF8ToString(pointer: getSecretViewKeyNative());
@ -213,8 +213,8 @@ Timer _updateSyncInfoTimer;
int _lastKnownBlockHeight = 0; int _lastKnownBlockHeight = 0;
setListeners(Future Function(int) onNewBlock, Future Function() onNeedToRefresh, void setListeners(Future Function(int) onNewBlock,
Future Function() onNewTransaction) { Future Function() onNeedToRefresh, Future Function() onNewTransaction) {
if (_updateSyncInfoTimer != null) { if (_updateSyncInfoTimer != null) {
_updateSyncInfoTimer.cancel(); _updateSyncInfoTimer.cancel();
} }
@ -240,25 +240,36 @@ setListeners(Future Function(int) onNewBlock, Future Function() onNeedToRefresh,
setListenerNative(); setListenerNative();
} }
closeListeners() { void closeListeners() {
if (_updateSyncInfoTimer != null) { if (_updateSyncInfoTimer != null) {
_updateSyncInfoTimer.cancel(); _updateSyncInfoTimer.cancel();
} }
} }
onStartup() => onStartupNative(); void onStartup() => onStartupNative();
_storeSync(_) => storeSync(); void _storeSync(Object _) => storeSync();
bool _setupNodeSync(Map args) => setupNodeSync(
address: args['address'],
login: args['login'] ?? '',
password: args['password'] ?? '',
useSSL: args['useSSL'],
isLightWallet: args['isLightWallet']);
bool _isConnected(_) => isConnectedSync();
int _getNodeHeight(_) => getNodeHeightSync();
startRefresh() => startRefreshSync(); bool _setupNodeSync(Map args) {
final address = args['address'] as String;
final login = (args['login'] ?? '') as String;
final password = (args['password'] ?? '') as String;
final useSSL = args['useSSL'] as bool;
final isLightWallet = args['isLightWallet'] as bool;
return setupNodeSync(
address: address,
login: login,
password: password,
useSSL: useSSL,
isLightWallet: isLightWallet);
}
bool _isConnected(Object _) => isConnectedSync();
int _getNodeHeight(Object _) => getNodeHeightSync();
void startRefresh() => startRefreshSync();
Future setupNode( Future setupNode(
{String address, {String address,
@ -280,4 +291,4 @@ Future<bool> isConnected() => compute(_isConnected, 0);
Future<int> getNodeHeight() => compute(_getNodeHeight, 0); Future<int> getNodeHeight() => compute(_getNodeHeight, 0);
rescanBlockchainAsync() => rescanBlockchainAsyncNative(); void rescanBlockchainAsync() => rescanBlockchainAsyncNative();

View file

@ -31,7 +31,7 @@ final loadWalletNative = moneroApi
.lookup<NativeFunction<load_wallet>>('load_wallet') .lookup<NativeFunction<load_wallet>>('load_wallet')
.asFunction<LoadWallet>(); .asFunction<LoadWallet>();
createWalletSync( void createWalletSync(
{String path, {String path,
String password, String password,
String language = 'English', String language = 'English',
@ -63,7 +63,7 @@ bool isWalletExistSync({String path}) {
return isExist; return isExist;
} }
restoreWalletFromSeedSync( void restoreWalletFromSeedSync(
{String path, {String path,
String password, String password,
String seed, String seed,
@ -92,7 +92,7 @@ restoreWalletFromSeedSync(
} }
} }
restoreWalletFromKeysSync( void restoreWalletFromKeysSync(
{String path, {String path,
String password, String password,
String language = 'English', String language = 'English',
@ -133,7 +133,7 @@ restoreWalletFromKeysSync(
} }
} }
loadWallet({String path, String password, int nettype = 0}) { void loadWallet({String path, String password, int nettype = 0}) {
final pathPointer = Utf8.toUtf8(path); final pathPointer = Utf8.toUtf8(path);
final passwordPointer = Utf8.toUtf8(password); final passwordPointer = Utf8.toUtf8(password);
@ -142,34 +142,52 @@ loadWallet({String path, String password, int nettype = 0}) {
free(passwordPointer); free(passwordPointer);
} }
_createWallet(args) => void _createWallet(Map<String, dynamic> args) {
createWalletSync(path: args['path'], password: args['password']); final path = args['path'] as String;
final password = args['password'] as String;
_restoreFromSeed(args) => restoreWalletFromSeedSync( createWalletSync(path: path, password: password);
path: args['path'], }
password: args['password'],
seed: args['seed'],
restoreHeight: args['restoreHeight']);
_restoreFromKeys(args) => restoreWalletFromKeysSync( void _restoreFromSeed(Map<String, dynamic> args) {
path: args['path'], final path = args['path'] as String;
password: args['password'], final password = args['password'] as String;
restoreHeight: args['restoreHeight'], final seed = args['seed'] as String;
address: args['address'], final restoreHeight = args['restoreHeight'] as int;
viewKey: args['viewKey'],
spendKey: args['spendKey']);
_openWallet(Map args) async => restoreWalletFromSeedSync(
path: path, password: password, seed: seed, restoreHeight: restoreHeight);
}
void _restoreFromKeys(Map<String, dynamic> args) {
final path = args['path'] as String;
final password = args['password'] as String;
final restoreHeight = args['restoreHeight'] as int;
final address = args['address'] as String;
final viewKey = args['viewKey'] as String;
final spendKey = args['spendKey'] as String;
restoreWalletFromKeysSync(
path: path,
password: password,
restoreHeight: restoreHeight,
address: address,
viewKey: viewKey,
spendKey: spendKey);
}
Future<void> _openWallet(Map<String, String> args) async =>
loadWallet(path: args['path'], password: args['password']); loadWallet(path: args['path'], password: args['password']);
bool _isWalletExist(String path) => isWalletExistSync(path: path); bool _isWalletExist(String path) => isWalletExistSync(path: path);
openWallet({String path, String password, int nettype = 0}) async => void openWallet({String path, String password, int nettype = 0}) async =>
loadWallet(path: path, password: password); loadWallet(path: path, password: password);
Future openWalletAsync(Map args) async => compute(_openWallet, args); Future<void> openWalletAsync(Map<String, String> args) async =>
compute(_openWallet, args);
Future createWallet( Future<void> createWallet(
{String path, {String path,
String password, String password,
String language = 'English', String language = 'English',

View file

@ -6,7 +6,7 @@ import 'package:provider/provider.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:hive/hive.dart'; import 'package:hive/hive.dart';
import 'package:cw_monero/wallet.dart' as moneroWallet; import 'package:cw_monero/wallet.dart' as monero_wallet;
import 'package:cake_wallet/router.dart'; import 'package:cake_wallet/router.dart';
import 'theme_changer.dart'; import 'theme_changer.dart';
import 'themes.dart'; import 'themes.dart';
@ -39,7 +39,7 @@ import 'package:cake_wallet/src/domain/common/language.dart';
void main() async { void main() async {
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
var appDir = await getApplicationDocumentsDirectory(); final appDir = await getApplicationDocumentsDirectory();
Hive.init(appDir.path); Hive.init(appDir.path);
Hive.registerAdapter(ContactAdapter(), 0); Hive.registerAdapter(ContactAdapter(), 0);
Hive.registerAdapter(NodeAdapter(), 1); Hive.registerAdapter(NodeAdapter(), 1);
@ -50,18 +50,20 @@ void main() async {
final secureStorage = FlutterSecureStorage(); final secureStorage = FlutterSecureStorage();
final transactionDescriptionsBoxKey = await getEncryptionKey( final transactionDescriptionsBoxKey = await getEncryptionKey(
secureStorage: secureStorage, forKey: 'transactionDescriptionsBoxKey'); // FIXME: Unnamed constant secureStorage: secureStorage,
forKey: 'transactionDescriptionsBoxKey'); // FIXME: Unnamed constant
final tradesBoxKey = await getEncryptionKey( final tradesBoxKey = await getEncryptionKey(
secureStorage: secureStorage, forKey: 'tradesBoxKey'); // FIXME: Unnamed constant secureStorage: secureStorage,
forKey: 'tradesBoxKey'); // FIXME: Unnamed constant
var contacts = await Hive.openBox<Contact>(Contact.boxName); final contacts = await Hive.openBox<Contact>(Contact.boxName);
var nodes = await Hive.openBox<Node>(Node.boxName); final nodes = await Hive.openBox<Node>(Node.boxName);
var transactionDescriptions = await Hive.openBox<TransactionDescription>( final transactionDescriptions = await Hive.openBox<TransactionDescription>(
TransactionDescription.boxName, TransactionDescription.boxName,
encryptionKey: transactionDescriptionsBoxKey); encryptionKey: transactionDescriptionsBoxKey);
var trades = final trades =
await Hive.openBox<Trade>(Trade.boxName, encryptionKey: tradesBoxKey); await Hive.openBox<Trade>(Trade.boxName, encryptionKey: tradesBoxKey);
var walletInfoSource = await Hive.openBox<WalletInfo>(WalletInfo.boxName); final walletInfoSource = await Hive.openBox<WalletInfo>(WalletInfo.boxName);
final sharedPreferences = await SharedPreferences.getInstance(); final sharedPreferences = await SharedPreferences.getInstance();
final walletService = WalletService(); final walletService = WalletService();
@ -124,7 +126,7 @@ void main() async {
], child: CakeWalletApp())); ], child: CakeWalletApp()));
} }
initialSetup( Future<void> initialSetup(
{WalletListService walletListService, {WalletListService walletListService,
SharedPreferences sharedPreferences, SharedPreferences sharedPreferences,
Box<Node> nodes, Box<Node> nodes,
@ -137,7 +139,7 @@ initialSetup(
sharedPreferences: sharedPreferences, sharedPreferences: sharedPreferences,
nodes: nodes); nodes: nodes);
await authStore.started(); await authStore.started();
moneroWallet.onStartup(); monero_wallet.onStartup();
} }
class CakeWalletApp extends StatelessWidget { class CakeWalletApp extends StatelessWidget {

View file

@ -19,6 +19,10 @@ import 'package:cake_wallet/src/domain/exchange/xmrto/xmrto_exchange_provider.da
import 'package:cake_wallet/src/domain/common/node.dart'; import 'package:cake_wallet/src/domain/common/node.dart';
import 'package:cake_wallet/src/domain/monero/transaction_description.dart'; import 'package:cake_wallet/src/domain/monero/transaction_description.dart';
import 'package:cake_wallet/src/domain/exchange/trade.dart'; import 'package:cake_wallet/src/domain/exchange/trade.dart';
import 'package:cake_wallet/src/domain/monero/account.dart';
import 'package:cake_wallet/src/domain/common/mnemotic_item.dart';
import 'package:cake_wallet/src/domain/common/transaction_info.dart';
import 'package:cake_wallet/src/domain/monero/subaddress.dart';
// MARK: Import stores // MARK: Import stores
@ -101,10 +105,10 @@ class Router {
Box<Trade> trades}) { Box<Trade> trades}) {
switch (settings.name) { switch (settings.name) {
case Routes.welcome: case Routes.welcome:
return MaterialPageRoute(builder: (_) => createWelcomePage()); return MaterialPageRoute<void>(builder: (_) => createWelcomePage());
case Routes.newWalletFromWelcome: case Routes.newWalletFromWelcome:
return CupertinoPageRoute( return CupertinoPageRoute<void>(
builder: (_) => Provider( builder: (_) => Provider(
create: (_) => UserStore( create: (_) => UserStore(
accountService: UserService( accountService: UserService(
@ -115,7 +119,7 @@ class Router {
Navigator.pushNamed(context, Routes.newWallet)))); Navigator.pushNamed(context, Routes.newWallet))));
case Routes.newWallet: case Routes.newWallet:
return CupertinoPageRoute( return CupertinoPageRoute<void>(
builder: builder:
(_) => (_) =>
ProxyProvider<AuthenticationStore, WalletCreationStore>( ProxyProvider<AuthenticationStore, WalletCreationStore>(
@ -132,10 +136,10 @@ class Router {
Function(BuildContext, String) callback; Function(BuildContext, String) callback;
if (settings.arguments is Function(BuildContext, String)) { if (settings.arguments is Function(BuildContext, String)) {
callback = settings.arguments; callback = settings.arguments as Function(BuildContext, String);
} }
return CupertinoPageRoute( return CupertinoPageRoute<void>(
builder: (_) => Provider( builder: (_) => Provider(
create: (_) => UserStore( create: (_) => UserStore(
accountService: UserService( accountService: UserService(
@ -147,13 +151,14 @@ class Router {
fullscreenDialog: true); fullscreenDialog: true);
case Routes.restoreOptions: case Routes.restoreOptions:
return CupertinoPageRoute(builder: (_) => RestoreOptionsPage()); return CupertinoPageRoute<void>(builder: (_) => RestoreOptionsPage());
case Routes.restoreWalletOptions: case Routes.restoreWalletOptions:
return CupertinoPageRoute(builder: (_) => RestoreWalletOptionsPage()); return CupertinoPageRoute<void>(
builder: (_) => RestoreWalletOptionsPage());
case Routes.restoreWalletOptionsFromWelcome: case Routes.restoreWalletOptionsFromWelcome:
return CupertinoPageRoute( return CupertinoPageRoute<void>(
builder: (_) => Provider( builder: (_) => Provider(
create: (_) => UserStore( create: (_) => UserStore(
accountService: UserService( accountService: UserService(
@ -164,14 +169,14 @@ class Router {
context, Routes.restoreWalletOptions)))); context, Routes.restoreWalletOptions))));
case Routes.seed: case Routes.seed:
return MaterialPageRoute( return MaterialPageRoute<void>(
builder: (_) => createSeedPage( builder: (_) => createSeedPage(
settingsStore: settingsStore, settingsStore: settingsStore,
walletService: walletService, walletService: walletService,
callback: settings.arguments)); callback: settings.arguments as void Function()));
case Routes.restoreWalletFromSeed: case Routes.restoreWalletFromSeed:
return CupertinoPageRoute( return CupertinoPageRoute<void>(
builder: (_) => builder: (_) =>
ProxyProvider<AuthenticationStore, WalletRestorationStore>( ProxyProvider<AuthenticationStore, WalletRestorationStore>(
update: (_, authStore, __) => WalletRestorationStore( update: (_, authStore, __) => WalletRestorationStore(
@ -184,7 +189,7 @@ class Router {
sharedPreferences: sharedPreferences))); sharedPreferences: sharedPreferences)));
case Routes.restoreWalletFromKeys: case Routes.restoreWalletFromKeys:
return CupertinoPageRoute( return CupertinoPageRoute<void>(
builder: (_) => builder: (_) =>
ProxyProvider<AuthenticationStore, WalletRestorationStore>( ProxyProvider<AuthenticationStore, WalletRestorationStore>(
update: (_, authStore, __) => WalletRestorationStore( update: (_, authStore, __) => WalletRestorationStore(
@ -197,7 +202,7 @@ class Router {
sharedPreferences: sharedPreferences))); sharedPreferences: sharedPreferences)));
case Routes.dashboard: case Routes.dashboard:
return CupertinoPageRoute( return CupertinoPageRoute<void>(
builder: (_) => createDashboardPage( builder: (_) => createDashboardPage(
walletService: walletService, walletService: walletService,
priceStore: priceStore, priceStore: priceStore,
@ -207,7 +212,7 @@ class Router {
walletStore: walletStore)); walletStore: walletStore));
case Routes.send: case Routes.send:
return CupertinoPageRoute( return CupertinoPageRoute<void>(
fullscreenDialog: true, fullscreenDialog: true,
builder: (_) => MultiProvider(providers: [ builder: (_) => MultiProvider(providers: [
ProxyProvider<SettingsStore, BalanceStore>( ProxyProvider<SettingsStore, BalanceStore>(
@ -227,7 +232,7 @@ class Router {
], child: SendPage())); ], child: SendPage()));
case Routes.receive: case Routes.receive:
return CupertinoPageRoute( return CupertinoPageRoute<void>(
fullscreenDialog: true, fullscreenDialog: true,
builder: (_) => MultiProvider(providers: [ builder: (_) => MultiProvider(providers: [
Provider( Provider(
@ -236,30 +241,30 @@ class Router {
], child: ReceivePage())); ], child: ReceivePage()));
case Routes.transactionDetails: case Routes.transactionDetails:
return CupertinoPageRoute( return CupertinoPageRoute<void>(
fullscreenDialog: true, fullscreenDialog: true,
builder: (_) => builder: (_) => TransactionDetailsPage(
TransactionDetailsPage(transactionInfo: settings.arguments)); transactionInfo: settings.arguments as TransactionInfo));
case Routes.newSubaddress: case Routes.newSubaddress:
return CupertinoPageRoute( return CupertinoPageRoute<void>(
builder: (_) => Provider( builder: (_) => Provider(
create: (_) => create: (_) =>
SubadrressCreationStore(walletService: walletService), SubadrressCreationStore(walletService: walletService),
child: NewSubaddressPage())); child: NewSubaddressPage()));
case Routes.disclaimer: case Routes.disclaimer:
return CupertinoPageRoute(builder: (_) => DisclaimerPage()); return CupertinoPageRoute<void>(builder: (_) => DisclaimerPage());
case Routes.readDisclaimer: case Routes.readDisclaimer:
return CupertinoPageRoute( return CupertinoPageRoute<void>(
builder: (_) => DisclaimerPage(isReadOnly: true)); builder: (_) => DisclaimerPage(isReadOnly: true));
case Routes.seedAlert: case Routes.seedAlert:
return CupertinoPageRoute(builder: (_) => SeedAlert()); return CupertinoPageRoute<void>(builder: (_) => SeedAlert());
case Routes.walletList: case Routes.walletList:
return MaterialPageRoute( return MaterialPageRoute<void>(
fullscreenDialog: true, fullscreenDialog: true,
builder: (_) => Provider( builder: (_) => Provider(
create: (_) => WalletListStore( create: (_) => WalletListStore(
@ -268,40 +273,43 @@ class Router {
child: WalletListPage())); child: WalletListPage()));
case Routes.auth: case Routes.auth:
return MaterialPageRoute( return MaterialPageRoute<void>(
fullscreenDialog: true, fullscreenDialog: true,
builder: (_) => Provider( builder: (_) => Provider(
create: (_) => AuthStore( create: (_) => AuthStore(
sharedPreferences: sharedPreferences, sharedPreferences: sharedPreferences,
userService: userService, userService: userService,
walletService: walletService), walletService: walletService),
child: AuthPage(onAuthenticationFinished: settings.arguments), child: AuthPage(
onAuthenticationFinished:
settings.arguments as OnAuthenticationFinished),
)); ));
case Routes.unlock: case Routes.unlock:
return MaterialPageRoute( return MaterialPageRoute<void>(
fullscreenDialog: true, fullscreenDialog: true,
builder: (_) => createUnlockPage( builder: (_) => createUnlockPage(
sharedPreferences: sharedPreferences, sharedPreferences: sharedPreferences,
userService: userService, userService: userService,
walletService: walletService, walletService: walletService,
onAuthenticationFinished: settings.arguments)); onAuthenticationFinished:
settings.arguments as OnAuthenticationFinished));
case Routes.nodeList: case Routes.nodeList:
return CupertinoPageRoute(builder: (context) { return CupertinoPageRoute<void>(builder: (context) {
return Provider( return Provider(
create: (_) => NodeListStore(nodesSource: nodes), create: (_) => NodeListStore(nodesSource: nodes),
child: NodeListPage()); child: NodeListPage());
}); });
case Routes.newNode: case Routes.newNode:
return CupertinoPageRoute( return CupertinoPageRoute<void>(
builder: (_) => Provider<NodeListStore>( builder: (_) => Provider<NodeListStore>(
create: (_) => NodeListStore(nodesSource: nodes), create: (_) => NodeListStore(nodesSource: nodes),
child: NewNodePage())); child: NewNodePage()));
case Routes.login: case Routes.login:
return CupertinoPageRoute(builder: (context) { return CupertinoPageRoute<void>(builder: (context) {
final authenticationStore = Provider.of<AuthenticationStore>(context); final authenticationStore = Provider.of<AuthenticationStore>(context);
return createLoginPage( return createLoginPage(
@ -313,7 +321,7 @@ class Router {
}); });
case Routes.accountList: case Routes.accountList:
return MaterialPageRoute( return MaterialPageRoute<void>(
builder: (context) { builder: (context) {
return MultiProvider(providers: [ return MultiProvider(providers: [
Provider( Provider(
@ -324,14 +332,14 @@ class Router {
fullscreenDialog: true); fullscreenDialog: true);
case Routes.accountCreation: case Routes.accountCreation:
return CupertinoPageRoute(builder: (context) { return CupertinoPageRoute<String>(builder: (context) {
return Provider( return Provider(
create: (_) => AccountListStore(walletService: walletService), create: (_) => AccountListStore(walletService: walletService),
child: AccountPage(account: settings.arguments)); child: AccountPage(account: settings.arguments as Account));
}); });
case Routes.addressBook: case Routes.addressBook:
return MaterialPageRoute(builder: (context) { return MaterialPageRoute<void>(builder: (context) {
return MultiProvider( return MultiProvider(
providers: [ providers: [
Provider( Provider(
@ -344,7 +352,7 @@ class Router {
}); });
case Routes.pickerAddressBook: case Routes.pickerAddressBook:
return MaterialPageRoute(builder: (context) { return MaterialPageRoute<void>(builder: (context) {
return MultiProvider( return MultiProvider(
providers: [ providers: [
Provider( Provider(
@ -357,7 +365,7 @@ class Router {
}); });
case Routes.addressBookAddContact: case Routes.addressBookAddContact:
return CupertinoPageRoute(builder: (context) { return CupertinoPageRoute<void>(builder: (context) {
return MultiProvider( return MultiProvider(
providers: [ providers: [
Provider( Provider(
@ -365,12 +373,12 @@ class Router {
AccountListStore(walletService: walletService)), AccountListStore(walletService: walletService)),
Provider(create: (_) => AddressBookStore(contacts: contacts)) Provider(create: (_) => AddressBookStore(contacts: contacts))
], ],
child: ContactPage(contact: settings.arguments), child: ContactPage(contact: settings.arguments as Contact),
); );
}); });
case Routes.showKeys: case Routes.showKeys:
return MaterialPageRoute( return MaterialPageRoute<void>(
builder: (context) { builder: (context) {
return Provider( return Provider(
create: (_) => WalletKeysStore(walletService: walletService), create: (_) => WalletKeysStore(walletService: walletService),
@ -380,12 +388,13 @@ class Router {
fullscreenDialog: true); fullscreenDialog: true);
case Routes.exchangeTrade: case Routes.exchangeTrade:
return CupertinoPageRoute( return CupertinoPageRoute<void>(
builder: (_) => MultiProvider( builder: (_) => MultiProvider(
providers: [ providers: [
ProxyProvider<SettingsStore, ExchangeTradeStore>( ProxyProvider<SettingsStore, ExchangeTradeStore>(
update: (_, settingsStore, __) => ExchangeTradeStore( update: (_, settingsStore, __) => ExchangeTradeStore(
trade: settings.arguments, walletStore: walletStore), trade: settings.arguments as Trade,
walletStore: walletStore),
), ),
ProxyProvider<SettingsStore, SendStore>( ProxyProvider<SettingsStore, SendStore>(
update: (_, settingsStore, __) => SendStore( update: (_, settingsStore, __) => SendStore(
@ -398,21 +407,22 @@ class Router {
)); ));
case Routes.exchangeConfirm: case Routes.exchangeConfirm:
return MaterialPageRoute( return MaterialPageRoute<void>(
builder: (_) => ExchangeConfirmPage(trade: settings.arguments)); builder: (_) =>
ExchangeConfirmPage(trade: settings.arguments as Trade));
case Routes.tradeDetails: case Routes.tradeDetails:
return MaterialPageRoute(builder: (context) { return MaterialPageRoute<void>(builder: (context) {
return MultiProvider(providers: [ return MultiProvider(providers: [
ProxyProvider<SettingsStore, ExchangeTradeStore>( ProxyProvider<SettingsStore, ExchangeTradeStore>(
update: (_, settingsStore, __) => ExchangeTradeStore( update: (_, settingsStore, __) => ExchangeTradeStore(
trade: settings.arguments, walletStore: walletStore), trade: settings.arguments as Trade, walletStore: walletStore),
) )
], child: TradeDetailsPage()); ], child: TradeDetailsPage());
}); });
case Routes.subaddressList: case Routes.subaddressList:
return MaterialPageRoute( return MaterialPageRoute<Subaddress>(
builder: (_) => MultiProvider(providers: [ builder: (_) => MultiProvider(providers: [
Provider( Provider(
create: (_) => create: (_) =>
@ -420,17 +430,18 @@ class Router {
], child: SubaddressListPage())); ], child: SubaddressListPage()));
case Routes.restoreWalletFromSeedDetails: case Routes.restoreWalletFromSeedDetails:
return CupertinoPageRoute( return CupertinoPageRoute<void>(
builder: (_) => builder: (_) =>
ProxyProvider<AuthenticationStore, WalletRestorationStore>( ProxyProvider<AuthenticationStore, WalletRestorationStore>(
update: (_, authStore, __) => WalletRestorationStore( update: (_, authStore, __) => WalletRestorationStore(
authStore: authStore, authStore: authStore,
sharedPreferences: sharedPreferences, sharedPreferences: sharedPreferences,
walletListService: walletListService, walletListService: walletListService,
seed: settings.arguments), seed: settings.arguments as List<MnemoticItem>),
child: RestoreWalletFromSeedDetailsPage())); child: RestoreWalletFromSeedDetailsPage()));
case Routes.exchange: case Routes.exchange:
return MaterialPageRoute( return MaterialPageRoute<void>(
builder: (_) => MultiProvider(providers: [ builder: (_) => MultiProvider(providers: [
Provider(create: (_) { Provider(create: (_) {
final xmrtoprovider = XMRTOExchangeProvider(); final xmrtoprovider = XMRTOExchangeProvider();
@ -449,25 +460,25 @@ class Router {
], child: ExchangePage())); ], child: ExchangePage()));
case Routes.settings: case Routes.settings:
return MaterialPageRoute( return MaterialPageRoute<void>(
builder: (_) => Provider( builder: (_) => Provider(
create: (_) => NodeListStore(nodesSource: nodes), create: (_) => NodeListStore(nodesSource: nodes),
child: SettingsPage())); child: SettingsPage()));
case Routes.rescan: case Routes.rescan:
return MaterialPageRoute( return MaterialPageRoute<void>(
builder: (_) => Provider( builder: (_) => Provider(
create: (_) => RescanWalletStore(walletService: walletService), create: (_) => RescanWalletStore(walletService: walletService),
child: RescanPage())); child: RescanPage()));
case Routes.faq: case Routes.faq:
return MaterialPageRoute(builder: (_) => FaqPage()); return MaterialPageRoute<void>(builder: (_) => FaqPage());
case Routes.changeLanguage: case Routes.changeLanguage:
return MaterialPageRoute(builder: (_) => ChangeLanguage()); return MaterialPageRoute<void>(builder: (_) => ChangeLanguage());
default: default:
return MaterialPageRoute( return MaterialPageRoute<void>(
builder: (_) => Scaffold( builder: (_) => Scaffold(
body: Center( body: Center(
child: Text(S.current.router_no_route(settings.name))), child: Text(S.current.router_no_route(settings.name))),

View file

@ -3,6 +3,9 @@ import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/domain/common/enumerable_item.dart'; import 'package:cake_wallet/src/domain/common/enumerable_item.dart';
class BalanceDisplayMode extends EnumerableItem<int> with Serializable<int> { class BalanceDisplayMode extends EnumerableItem<int> with Serializable<int> {
const BalanceDisplayMode({@required String title, @required int raw})
: super(title: title, raw: raw);
static const all = [ static const all = [
BalanceDisplayMode.fullBalance, BalanceDisplayMode.fullBalance,
BalanceDisplayMode.availableBalance, BalanceDisplayMode.availableBalance,
@ -27,9 +30,6 @@ class BalanceDisplayMode extends EnumerableItem<int> with Serializable<int> {
} }
} }
const BalanceDisplayMode({@required String title, @required int raw})
: super(title: title, raw: raw);
@override @override
String toString() { String toString() {
switch (this) { switch (this) {

View file

@ -6,6 +6,9 @@ part 'contact.g.dart';
@HiveType() @HiveType()
class Contact extends HiveObject { class Contact extends HiveObject {
Contact({@required this.name, @required this.address, CryptoCurrency type})
: raw = type?.raw;
static const boxName = 'Contacts'; static const boxName = 'Contacts';
@HiveField(0) @HiveField(0)
@ -19,12 +22,6 @@ class Contact extends HiveObject {
CryptoCurrency get type => CryptoCurrency.deserialize(raw: raw); CryptoCurrency get type => CryptoCurrency.deserialize(raw: raw);
Contact( void updateCryptoCurrency({@required CryptoCurrency currency}) =>
{@required this.name,
@required this.address,
CryptoCurrency type})
: raw = type?.raw;
updateCryptoCurrency({@required CryptoCurrency currency}) =>
raw = currency.raw; raw = currency.raw;
} }

View file

@ -5,6 +5,9 @@ part 'crypto_currency.g.dart';
@HiveType() @HiveType()
class CryptoCurrency extends EnumerableItem<int> with Serializable<int> { class CryptoCurrency extends EnumerableItem<int> with Serializable<int> {
const CryptoCurrency({final String title, final int raw})
: super(title: title, raw: raw);
static const all = [ static const all = [
CryptoCurrency.xmr, CryptoCurrency.xmr,
CryptoCurrency.btc, CryptoCurrency.btc,
@ -58,9 +61,6 @@ class CryptoCurrency extends EnumerableItem<int> with Serializable<int> {
} }
} }
const CryptoCurrency({final String title, final int raw})
: super(title: title, raw: raw);
@override @override
String toString() => title; String toString() => title;
} }

View file

@ -11,7 +11,7 @@ Future defaultSettingsMigration(
{@required int version, {@required int version,
@required SharedPreferences sharedPreferences, @required SharedPreferences sharedPreferences,
@required Box<Node> nodes}) async { @required Box<Node> nodes}) async {
int currentVersion = final currentVersion =
sharedPreferences.getInt('current_default_settings_migration_version') ?? sharedPreferences.getInt('current_default_settings_migration_version') ??
0; 0;
@ -22,17 +22,17 @@ Future defaultSettingsMigration(
try { try {
switch (version) { switch (version) {
case 1: case 1:
sharedPreferences.setString( await sharedPreferences.setString(
'current_fiat_currency', FiatCurrency.usd.toString()); 'current_fiat_currency', FiatCurrency.usd.toString());
sharedPreferences.setInt( await sharedPreferences.setInt(
'current_fee_priority', TransactionPriority.standart.raw); 'current_fee_priority', TransactionPriority.standart.raw);
sharedPreferences.setInt('current_balance_display_mode', await sharedPreferences.setInt('current_balance_display_mode',
BalanceDisplayMode.availableBalance.raw); BalanceDisplayMode.availableBalance.raw);
sharedPreferences.setInt( await sharedPreferences.setInt(
'current_default_settings_migration_version', 1); 'current_default_settings_migration_version', 1);
sharedPreferences.setBool('save_recipient_address', false); await sharedPreferences.setBool('save_recipient_address', false);
await resetToDefault(nodes); await resetToDefault(nodes);
sharedPreferences.setInt('current_node_id', 0); await sharedPreferences.setInt('current_node_id', 0);
break; break;
default: default:
break; break;
@ -41,6 +41,6 @@ Future defaultSettingsMigration(
print('Migration error: ${e.toString()}'); print('Migration error: ${e.toString()}');
} }
sharedPreferences.setInt( await sharedPreferences.setInt(
'current_default_settings_migration_version', version); 'current_default_settings_migration_version', version);
} }

View file

@ -1,5 +1,5 @@
import 'dart:convert'; import 'dart:convert';
import 'package:dio/dio.dart' as Dio; import 'package:dio/dio.dart' as __dio;
import 'package:crypto/crypto.dart' as crypto; import 'package:crypto/crypto.dart' as crypto;
import 'dart:math' as math; import 'dart:math' as math;
@ -8,13 +8,13 @@ class DigestRequest {
String generateCnonce() { String generateCnonce() {
final rnd = math.Random.secure(); final rnd = math.Random.secure();
var values = List<int>.generate(32, (i) => rnd.nextInt(256)); final values = List<int>.generate(32, (i) => rnd.nextInt(256));
return base64Url.encode(values).substring(0, 8); return base64Url.encode(values).substring(0, 8);
} }
String generateHA1({String realm, String username, String password}) { String generateHA1({String realm, String username, String password}) {
final ha1CredentialsData = final ha1CredentialsData =
Utf8Encoder().convert('$username:$realm:$password'); Utf8Encoder().convert('$username:$realm:$password');
final ha1 = md5.convert(ha1CredentialsData).toString(); final ha1 = md5.convert(ha1CredentialsData).toString();
return ha1; return ha1;
@ -29,13 +29,13 @@ class DigestRequest {
String generateResponseString( String generateResponseString(
{String ha1, {String ha1,
String ha2, String ha2,
String nonce, String nonce,
String nonceCount, String nonceCount,
String cnonce, String cnonce,
String qop}) { String qop}) {
final responseData = final responseData =
Utf8Encoder().convert('$ha1:$nonce:$nonceCount:$cnonce:$qop:$ha2'); Utf8Encoder().convert('$ha1:$nonce:$nonceCount:$cnonce:$qop:$ha2');
final response = md5.convert(responseData).toString(); final response = md5.convert(responseData).toString();
return response; return response;
@ -43,8 +43,8 @@ class DigestRequest {
Map<String, String> parsetAuthorizationHeader({String source}) { Map<String, String> parsetAuthorizationHeader({String source}) {
final authHeaderParts = final authHeaderParts =
source.substring(7).split(',').map((item) => item.trim()); source.substring(7).split(',').map((item) => item.trim());
var authenticate = Map<String, String>(); final authenticate = Map<String, String>();
for (final part in authHeaderParts) { for (final part in authHeaderParts) {
final kv = part.split('='); final kv = part.split('=');
@ -55,30 +55,25 @@ class DigestRequest {
return authenticate; return authenticate;
} }
Future<Dio.Response> request( Future<__dio.Response> request(
{String uri, String login, String password}) async { {String uri, String login, String password}) async {
final path = '/json_rpc'; const path = '/json_rpc';
final method = 'POST'; const method = 'POST';
final url = Uri.http(uri, path); final url = Uri.http(uri, path);
final dio = Dio.Dio(); final dio = __dio.Dio();
final headers = {'Content-type': 'application/json'};
Map<String, String> headers = {'Content-type': 'application/json'}; final body =
String body = json.encode({"jsonrpc": "2.0", "id": "0", "method": "get_info"});
json.encode({"jsonrpc": "2.0", "id": "0", "method": "get_info"}); final credentialsResponse = await dio.post<Object>(url.toString(),
options: __dio.Options(headers: headers, validateStatus: (_) => true));
var credentialsResponse = await dio.post(url.toString(), final authenticate = parsetAuthorizationHeader(
options: Dio.Options(headers: headers, validateStatus: (_) => true)); source: credentialsResponse.headers['www-authenticate'].first);
var resHeaeders = credentialsResponse.headers;
final authenticate =
parsetAuthorizationHeader(source: resHeaeders['www-authenticate'].first);
final qop = authenticate['qop']; final qop = authenticate['qop'];
final algorithm = 'MD5'; final algorithm = 'MD5';
final realm = 'monero-rpc'; final realm = 'monero-rpc';
final nonce = authenticate['nonce']; final nonce = authenticate['nonce'];
final cnonce = generateCnonce(); final cnonce = generateCnonce();
final nonceCount = '00000001';
var nonceCount = '00000001';
final ha1 = generateHA1(realm: realm, username: login, password: password); final ha1 = generateHA1(realm: realm, username: login, password: password);
final ha2 = generateHA2(method: method, uri: path); final ha2 = generateHA2(method: method, uri: path);
final response = generateResponseString( final response = generateResponseString(
@ -92,11 +87,10 @@ class DigestRequest {
final authorizationHeaders = { final authorizationHeaders = {
'Content-type': 'application/json', 'Content-type': 'application/json',
'Authorization': 'Authorization':
'Digest username="$login",realm="$realm",nonce="$nonce",uri="$path",algorithm="$algorithm",qop=$qop,nc=$nonceCount,cnonce="$cnonce",response="$response"' 'Digest username="$login",realm="$realm",nonce="$nonce",uri="$path",algorithm="$algorithm",qop=$qop,nc=$nonceCount,cnonce="$cnonce",response="$response"'
}; };
final res = await dio.post(url.toString(), return await dio.post<Object>(url.toString(),
options: Dio.Options(headers: authorizationHeaders), data: body); options: __dio.Options(headers: authorizationHeaders), data: body);
return res;
} }
} }

View file

@ -1,11 +1,11 @@
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
abstract class EnumerableItem<T> { abstract class EnumerableItem<T> {
const EnumerableItem({@required this.title, @required this.raw});
final T raw; final T raw;
final String title; final String title;
const EnumerableItem({@required this.title, @required this.raw});
@override @override
String toString() => title; String toString() => title;
} }

View file

@ -20,12 +20,12 @@ Future<double> fetchPriceFor({CryptoCurrency crypto, FiatCurrency fiat}) async {
return 0.0; return 0.0;
} }
final responseJSON = json.decode(response.body); final responseJSON = json.decode(response.body) as Map<String, dynamic>;
final data = responseJSON['data']; final data = responseJSON['data'] as List<Map<String, dynamic>>;
for (final item in data) { for (final item in data) {
if (item['symbol'] == cryptoToString(crypto)) { if (item['symbol'] == cryptoToString(crypto)) {
price = item['quote'][fiatStringified]['price']; price = item['quote'][fiatStringified]['price'] as double;
break; break;
} }
} }

View file

@ -1,6 +1,11 @@
import 'package:cake_wallet/src/domain/common/enumerable_item.dart'; import 'package:cake_wallet/src/domain/common/enumerable_item.dart';
class FiatCurrency extends EnumerableItem<String> with Serializable<String> { class FiatCurrency extends EnumerableItem<String> with Serializable<String> {
const FiatCurrency({String symbol}) : super(title: symbol, raw: symbol);
@override
bool operator ==(Object other) => other is FiatCurrency && other.raw == raw;
static const all = [ static const all = [
FiatCurrency.aud, FiatCurrency.aud,
FiatCurrency.bgn, FiatCurrency.bgn,
@ -71,10 +76,6 @@ class FiatCurrency extends EnumerableItem<String> with Serializable<String> {
static const zar = FiatCurrency(symbol: 'ZAR'); static const zar = FiatCurrency(symbol: 'ZAR');
static const vef = FiatCurrency(symbol: 'VEF'); static const vef = FiatCurrency(symbol: 'VEF');
const FiatCurrency({String symbol}) : super(title: symbol, raw: symbol);
operator ==(o) => o is FiatCurrency && o.raw == raw;
@override @override
int get hashCode => raw.hashCode ^ title.hashCode; int get hashCode => raw.hashCode ^ title.hashCode;
} }

View file

@ -1,16 +1,14 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class Language with ChangeNotifier { class Language with ChangeNotifier {
Language(this._currentLanguage);
String _currentLanguage; String _currentLanguage;
Language(this._currentLanguage); String getCurrentLanguage() => _currentLanguage;
getCurrentLanguage() => _currentLanguage; void setCurrentLanguage(String language) {
setCurrentLanguage(String language){
_currentLanguage = language; _currentLanguage = language;
notifyListeners(); notifyListeners();
} }
} }

View file

@ -1,13 +1,11 @@
class MnemoticItem { class MnemoticItem {
MnemoticItem({String text, this.dic}) : _text = text;
String get text => _text; String get text => _text;
final List<String> dic; final List<String> dic;
String _text; String _text;
MnemoticItem({String text, this.dic}) {
_text = text;
}
bool isCorrect() => dic.contains(text); bool isCorrect() => dic.contains(text);
void changeText(String text) { void changeText(String text) {

View file

@ -8,6 +8,13 @@ part 'node.g.dart';
@HiveType() @HiveType()
class Node extends HiveObject { class Node extends HiveObject {
Node({@required this.uri, this.login, this.password});
Node.fromMap(Map map)
: uri = (map['uri'] ?? '') as String,
login = map['login'] as String,
password = map['password'] as String;
static const boxName = 'Nodes'; static const boxName = 'Nodes';
@HiveField(0) @HiveField(0)
@ -19,31 +26,24 @@ class Node extends HiveObject {
@HiveField(2) @HiveField(2)
String password; String password;
Node({@required this.uri, this.login, this.password});
Node.fromMap(Map map)
: uri = map['uri'] ?? '',
login = map['login'],
password = map['password'];
Future<bool> requestNode(String uri, {String login, String password}) async { Future<bool> requestNode(String uri, {String login, String password}) async {
var resBody; Map<String, dynamic> resBody;
if (login != null && password != null) { if (login != null && password != null) {
final digestRequest = DigestRequest(); final digestRequest = DigestRequest();
var response = await digestRequest.request( final response = await digestRequest.request(
uri: uri, login: login, password: password); uri: uri, login: login, password: password);
resBody = response.data; resBody = response.data as Map<String, dynamic>;
} else { } else {
final url = Uri.http(uri, '/json_rpc'); final url = Uri.http(uri, '/json_rpc');
Map<String, String> headers = {'Content-type': 'application/json'}; final headers = {'Content-type': 'application/json'};
String body = final body =
json.encode({"jsonrpc": "2.0", "id": "0", "method": "get_info"}); json.encode({"jsonrpc": "2.0", "id": "0", "method": "get_info"});
var response = final response =
await http.post(url.toString(), headers: headers, body: body); await http.post(url.toString(), headers: headers, body: body);
resBody = json.decode(response.body); resBody = json.decode(response.body) as Map<String, dynamic>;
} }
return !resBody["result"]["offline"]; return !(resBody["result"]["offline"] as bool);
} }
} }

View file

@ -4,16 +4,17 @@ import "package:yaml/yaml.dart";
import 'package:cake_wallet/src/domain/common/node.dart'; import 'package:cake_wallet/src/domain/common/node.dart';
Future<List<Node>> loadDefaultNodes() async { Future<List<Node>> loadDefaultNodes() async {
String nodesRaw = await rootBundle.loadString('assets/node_list.yml'); final nodesRaw = await rootBundle.loadString('assets/node_list.yml');
List nodes = loadYaml(nodesRaw); final nodes = loadYaml(nodesRaw) as List<Map<dynamic, dynamic>>;
return nodes.map((raw) => Node.fromMap(raw)).toList(); return nodes.map((raw) => Node.fromMap(raw)).toList();
} }
Future resetToDefault(Box<Node> nodeSource) async { Future resetToDefault(Box<Node> nodeSource) async {
final nodes = await loadDefaultNodes(); final nodes = await loadDefaultNodes();
await nodeSource.clear(); final enteties = Map<int, Node>();
Map<int, Node> enteties = {}; await nodeSource.clear();
for (var i = 0; i < nodes.length; i++) { for (var i = 0; i < nodes.length; i++) {
enteties[i] = nodes[i]; enteties[i] = nodes[i];

View file

@ -1,15 +1,9 @@
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:cw_monero/transaction_history.dart' as transactionHistory; import 'package:cw_monero/transaction_history.dart' as transaction_history;
import 'package:cw_monero/structs/pending_transaction.dart'; import 'package:cw_monero/structs/pending_transaction.dart';
import 'package:cake_wallet/src/domain/monero/monero_amount_format.dart'; import 'package:cake_wallet/src/domain/monero/monero_amount_format.dart';
class PendingTransaction { class PendingTransaction {
final String amount;
final String fee;
final String hash;
int _pointerAddress;
PendingTransaction( PendingTransaction(
{@required this.amount, @required this.fee, @required this.hash}); {@required this.amount, @required this.fee, @required this.hash});
@ -20,6 +14,12 @@ class PendingTransaction {
hash = transactionDescription.hash, hash = transactionDescription.hash,
_pointerAddress = transactionDescription.pointerAddress; _pointerAddress = transactionDescription.pointerAddress;
Future commit() async => transactionHistory final String amount;
final String fee;
final String hash;
int _pointerAddress;
Future<void> commit() async => transaction_history
.commitTransactionFromPointerAddress(address: _pointerAddress); .commitTransactionFromPointerAddress(address: _pointerAddress);
} }

View file

@ -10,6 +10,6 @@ Future<String> presentQRScanner() async {
return result; return result;
} catch (e) { } catch (e) {
isQrScannerShown = false; isQrScannerShown = false;
throw e; rethrow;
} }
} }

View file

@ -9,18 +9,20 @@ abstract class SyncStatus {
} }
class SyncingSyncStatus extends SyncStatus { class SyncingSyncStatus extends SyncStatus {
SyncingSyncStatus(this.height, this.blockchainHeight, this.refreshHeight);
final int height; final int height;
final int blockchainHeight; final int blockchainHeight;
final int refreshHeight; final int refreshHeight;
SyncingSyncStatus(this.height, this.blockchainHeight, this.refreshHeight); @override
double progress() { double progress() {
final line = blockchainHeight - refreshHeight; final line = blockchainHeight - refreshHeight;
final diff = line - (blockchainHeight - height); final diff = line - (blockchainHeight - height);
return diff <= 0 ? 0.0 : diff / line; return diff <= 0 ? 0.0 : diff / line;
} }
@override
String title() => S.current.sync_status_syncronizing; String title() => S.current.sync_status_syncronizing;
@override @override
@ -28,39 +30,51 @@ class SyncingSyncStatus extends SyncStatus {
} }
class SyncedSyncStatus extends SyncStatus { class SyncedSyncStatus extends SyncStatus {
@override
double progress() => 1.0; double progress() => 1.0;
@override
String title() => S.current.sync_status_syncronized; String title() => S.current.sync_status_syncronized;
} }
class NotConnectedSyncStatus extends SyncStatus { class NotConnectedSyncStatus extends SyncStatus {
const NotConnectedSyncStatus(); const NotConnectedSyncStatus();
@override
double progress() => 0.0; double progress() => 0.0;
@override
String title() => S.current.sync_status_not_connected; String title() => S.current.sync_status_not_connected;
} }
class StartingSyncStatus extends SyncStatus { class StartingSyncStatus extends SyncStatus {
@override
double progress() => 0.0; double progress() => 0.0;
@override
String title() => S.current.sync_status_starting_sync; String title() => S.current.sync_status_starting_sync;
} }
class FailedSyncStatus extends SyncStatus { class FailedSyncStatus extends SyncStatus {
@override
double progress() => 1.0; double progress() => 1.0;
@override
String title() => S.current.sync_status_failed_connect; String title() => S.current.sync_status_failed_connect;
} }
class ConnectingSyncStatus extends SyncStatus { class ConnectingSyncStatus extends SyncStatus {
@override
double progress() => 0.0; double progress() => 0.0;
@override
String title() => S.current.sync_status_connecting; String title() => S.current.sync_status_connecting;
} }
class ConnectedSyncStatus extends SyncStatus { class ConnectedSyncStatus extends SyncStatus {
@override
double progress() => 0.0; double progress() => 0.0;
@override
String title() => S.current.sync_status_connected; String title() => S.current.sync_status_connected;
} }

View file

@ -4,26 +4,20 @@ import 'package:cake_wallet/src/domain/common/parseBoolFromString.dart';
import 'package:cake_wallet/src/domain/common/transaction_direction.dart'; import 'package:cake_wallet/src/domain/common/transaction_direction.dart';
class TransactionInfo { class TransactionInfo {
final String id; TransactionInfo(this.id, this.height, this.direction, this.date,
final int height; this.isPending, this.amount, this.accountIndex);
final TransactionDirection direction;
final DateTime date;
final int accountIndex;
final bool isPending;
final int amount;
String recipientAddress;
String _fiatAmount;
TransactionInfo.fromMap(Map map) TransactionInfo.fromMap(Map map)
: id = map['hash'] ?? '', : id = (map['hash'] ?? '') as String,
height = map['height'] ?? '', height = (map['height'] ?? 0) as int,
direction = parseTransactionDirectionFromNumber(map['direction']) ?? direction =
TransactionDirection.incoming, parseTransactionDirectionFromNumber(map['direction'] as String) ??
TransactionDirection.incoming,
date = DateTime.fromMillisecondsSinceEpoch( date = DateTime.fromMillisecondsSinceEpoch(
(int.parse(map['timestamp']) ?? 0) * 1000), (int.parse(map['timestamp'] as String) ?? 0) * 1000),
isPending = parseBoolFromString(map['isPending']), isPending = parseBoolFromString(map['isPending'] as String),
amount = map['amount'], amount = map['amount'] as int,
accountIndex = int.parse(map['accountIndex']); accountIndex = int.parse(map['accountIndex'] as String);
TransactionInfo.fromRow(TransactionInfoRow row) TransactionInfo.fromRow(TransactionInfoRow row)
: id = row.getHash(), : id = row.getHash(),
@ -35,12 +29,20 @@ class TransactionInfo {
amount = row.getAmount(), amount = row.getAmount(),
accountIndex = row.subaddrAccount; accountIndex = row.subaddrAccount;
TransactionInfo(this.id, this.height, this.direction, this.date, final String id;
this.isPending, this.amount, this.accountIndex); final int height;
final TransactionDirection direction;
final DateTime date;
final int accountIndex;
final bool isPending;
final int amount;
String recipientAddress;
String _fiatAmount;
String amountFormatted() => '${moneroAmountToString(amount: amount)} XMR'; String amountFormatted() => '${moneroAmountToString(amount: amount)} XMR';
String fiatAmount() => _fiatAmount ?? ''; String fiatAmount() => _fiatAmount ?? '';
changeFiatAmount(String amount) => _fiatAmount = amount; void changeFiatAmount(String amount) => _fiatAmount = amount;
} }

View file

@ -2,6 +2,9 @@ import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/domain/common/enumerable_item.dart'; import 'package:cake_wallet/src/domain/common/enumerable_item.dart';
class TransactionPriority extends EnumerableItem<int> with Serializable<int> { class TransactionPriority extends EnumerableItem<int> with Serializable<int> {
const TransactionPriority({String title, int raw})
: super(title: title, raw: raw);
static const all = [ static const all = [
TransactionPriority.slow, TransactionPriority.slow,
TransactionPriority.regular, TransactionPriority.regular,
@ -33,9 +36,6 @@ class TransactionPriority extends EnumerableItem<int> with Serializable<int> {
} }
} }
const TransactionPriority({String title, int raw})
: super(title: title, raw: raw);
@override @override
String toString() { String toString() {
switch (this) { switch (this) {

View file

@ -1,8 +1,8 @@
import 'package:cake_wallet/src/domain/common/wallet_type.dart'; import 'package:cake_wallet/src/domain/common/wallet_type.dart';
class WalletDescription { class WalletDescription {
WalletDescription({this.name, this.type});
final String name; final String name;
final WalletType type; final WalletType type;
WalletDescription({this.name, this.type});
} }

View file

@ -5,6 +5,9 @@ part 'wallet_info.g.dart';
@HiveType() @HiveType()
class WalletInfo extends HiveObject { class WalletInfo extends HiveObject {
WalletInfo(
{this.id, this.name, this.type, this.isRecovery, this.restoreHeight});
static const boxName = 'WalletInfo'; static const boxName = 'WalletInfo';
@HiveField(0) @HiveField(0)
@ -21,7 +24,4 @@ class WalletInfo extends HiveObject {
@HiveField(4) @HiveField(4)
int restoreHeight; int restoreHeight;
WalletInfo(
{this.id, this.name, this.type, this.isRecovery, this.restoreHeight});
} }

View file

@ -15,45 +15,50 @@ import 'package:cake_wallet/src/domain/exchange/exchange_provider_description.da
import 'package:cake_wallet/src/domain/exchange/trade_not_created_exeption.dart'; import 'package:cake_wallet/src/domain/exchange/trade_not_created_exeption.dart';
class ChangeNowExchangeProvider extends ExchangeProvider { class ChangeNowExchangeProvider extends ExchangeProvider {
ChangeNowExchangeProvider()
: super(
pairList: CryptoCurrency.all
.map((i) {
return CryptoCurrency.all.map((k) {
if (i == CryptoCurrency.btc && k == CryptoCurrency.xmr) {
return ExchangePair(from: i, to: k, reverse: false);
}
if (i == CryptoCurrency.xmr && k == CryptoCurrency.btc) {
return null;
}
return ExchangePair(from: i, to: k, reverse: true);
}).where((c) => c != null);
})
.expand((i) => i)
.toList());
static const apiUri = 'https://changenow.io/api/v1'; static const apiUri = 'https://changenow.io/api/v1';
static const apiKey = secrets.change_now_api_key; static const apiKey = secrets.change_now_api_key;
static const _exchangeAmountUriSufix = '/exchange-amount/'; static const _exchangeAmountUriSufix = '/exchange-amount/';
static const _transactionsUriSufix = '/transactions/'; static const _transactionsUriSufix = '/transactions/';
static const _minAmountUriSufix = '/min-amount/'; static const _minAmountUriSufix = '/min-amount/';
@override
String get title => 'ChangeNOW'; String get title => 'ChangeNOW';
@override
ExchangeProviderDescription get description => ExchangeProviderDescription get description =>
ExchangeProviderDescription.changeNow; ExchangeProviderDescription.changeNow;
ChangeNowExchangeProvider() { @override
pairList = CryptoCurrency.all
.map((i) {
return CryptoCurrency.all.map((k) {
if (i == CryptoCurrency.btc && k == CryptoCurrency.xmr) {
return ExchangePair(from: i, to: k, reverse: false);
}
if (i == CryptoCurrency.xmr && k == CryptoCurrency.btc) {
return null;
}
return ExchangePair(from: i, to: k, reverse: true);
}).where((c) => c != null);
})
.expand((i) => i)
.toList();
}
Future<Limits> fetchLimits({CryptoCurrency from, CryptoCurrency to}) async { Future<Limits> fetchLimits({CryptoCurrency from, CryptoCurrency to}) async {
final symbol = from.toString() + '_' + to.toString(); final symbol = from.toString() + '_' + to.toString();
final url = apiUri + _minAmountUriSufix + symbol; final url = apiUri + _minAmountUriSufix + symbol;
final response = await get(url); final response = await get(url);
final responseJSON = json.decode(response.body); final responseJSON = json.decode(response.body) as Map<String, dynamic>;
final double min = responseJSON['minAmount']; final min = responseJSON['minAmount'] as double;
return Limits(min: min, max: null); return Limits(min: min, max: null);
} }
@override
Future<Trade> createTrade({TradeRequest request}) async { Future<Trade> createTrade({TradeRequest request}) async {
const url = apiUri + _transactionsUriSufix + apiKey; const url = apiUri + _transactionsUriSufix + apiKey;
final _request = request as ChangeNowRequest; final _request = request as ChangeNowRequest;
@ -70,37 +75,44 @@ class ChangeNowExchangeProvider extends ExchangeProvider {
if (response.statusCode != 200) { if (response.statusCode != 200) {
if (response.statusCode == 400) { if (response.statusCode == 400) {
final responseJSON = json.decode(response.body); final responseJSON = json.decode(response.body) as Map<String, dynamic>;
final error = responseJSON['message']; final error = responseJSON['message'] as String;
throw TradeNotCreatedException(description, description: error); throw TradeNotCreatedException(description, description: error);
} }
throw TradeNotCreatedException(description); throw TradeNotCreatedException(description);
} }
final responseJSON = json.decode(response.body); final responseJSON = json.decode(response.body) as Map<String, dynamic>;
final id = responseJSON['id'] as String;
final inputAddress = responseJSON['payinAddress'] as String;
final refundAddress = responseJSON['refundAddress'] as String;
final extraId = responseJSON['payinExtraId'] as String;
return Trade( return Trade(
id: responseJSON['id'], id: id,
from: _request.from, from: _request.from,
to: _request.to, to: _request.to,
provider: description, provider: description,
inputAddress: responseJSON['payinAddress'], inputAddress: inputAddress,
refundAddress: responseJSON['refundAddress'], refundAddress: refundAddress,
extraId: responseJSON["payinExtraId"], extraId: extraId,
createdAt: DateTime.now(), createdAt: DateTime.now(),
amount: _request.amount, amount: _request.amount,
state: TradeState.created); state: TradeState.created);
} }
@override
Future<Trade> findTradeById({@required String id}) async { Future<Trade> findTradeById({@required String id}) async {
final url = apiUri + _transactionsUriSufix + id + '/' + apiKey; final url = apiUri + _transactionsUriSufix + id + '/' + apiKey;
final response = await get(url); final response = await get(url);
if (response.statusCode != 200) { if (response.statusCode != 200) {
if (response.statusCode == 400) { if (response.statusCode == 400) {
final responseJSON = json.decode(response.body); final responseJSON = json.decode(response.body) as Map<String, dynamic>;
final error = responseJSON['message']; final error = responseJSON['message'] as String;
throw TradeNotFoundException(id, throw TradeNotFoundException(id,
provider: description, description: error); provider: description, description: error);
} }
@ -108,20 +120,31 @@ class ChangeNowExchangeProvider extends ExchangeProvider {
throw TradeNotFoundException(id, provider: description); throw TradeNotFoundException(id, provider: description);
} }
final responseJSON = json.decode(response.body); final responseJSON = json.decode(response.body) as Map<String, dynamic>;
final fromCurrency = responseJSON['fromCurrency'] as String;
final from = CryptoCurrency.fromString(fromCurrency);
final toCurrency = responseJSON['toCurrency'] as String;
final to = CryptoCurrency.fromString(toCurrency);
final inputAddress = responseJSON['payinAddress'] as String;
final expectedSendAmount = responseJSON['expectedSendAmount'].toString();
final status = responseJSON['status'] as String;
final state = TradeState.deserialize(raw: status);
final extraId = responseJSON['payinExtraId'] as String;
final outputTransaction = responseJSON['payoutHash'] as String;
return Trade( return Trade(
id: id, id: id,
from: CryptoCurrency.fromString(responseJSON['fromCurrency']), from: from,
to: CryptoCurrency.fromString(responseJSON['toCurrency']), to: to,
provider: description, provider: description,
inputAddress: responseJSON['payinAddress'], inputAddress: inputAddress,
amount: responseJSON['expectedSendAmount'].toString(), amount: expectedSendAmount,
state: TradeState.deserialize(raw: responseJSON['status']), state: state,
extraId: responseJSON['payinExtraId'], extraId: extraId,
outputTransaction: responseJSON['payoutHash']); outputTransaction: outputTransaction);
} }
@override
Future<double> calculateAmount( Future<double> calculateAmount(
{CryptoCurrency from, CryptoCurrency to, double amount}) async { {CryptoCurrency from, CryptoCurrency to, double amount}) async {
final url = apiUri + final url = apiUri +
@ -132,8 +155,9 @@ class ChangeNowExchangeProvider extends ExchangeProvider {
'_' + '_' +
to.toString(); to.toString();
final response = await get(url); final response = await get(url);
final responseJSON = json.decode(response.body); final responseJSON = json.decode(response.body) as Map<String, dynamic>;
final estimatedAmount = responseJSON['estimatedAmount'] as double;
return responseJSON['estimatedAmount']; return estimatedAmount;
} }
} }

View file

@ -3,16 +3,16 @@ import 'package:cake_wallet/src/domain/common/crypto_currency.dart';
import 'package:cake_wallet/src/domain/exchange/trade_request.dart'; import 'package:cake_wallet/src/domain/exchange/trade_request.dart';
class ChangeNowRequest extends TradeRequest { class ChangeNowRequest extends TradeRequest {
CryptoCurrency from;
CryptoCurrency to;
String address;
String amount;
String refundAddress;
ChangeNowRequest( ChangeNowRequest(
{@required this.from, {@required this.from,
@required this.to, @required this.to,
@required this.address, @required this.address,
@required this.amount, @required this.amount,
@required this.refundAddress}); @required this.refundAddress});
CryptoCurrency from;
CryptoCurrency to;
String address;
String amount;
String refundAddress;
} }

View file

@ -1,9 +1,9 @@
import 'package:cake_wallet/src/domain/common/crypto_currency.dart'; import 'package:cake_wallet/src/domain/common/crypto_currency.dart';
class ExchangePair { class ExchangePair {
ExchangePair({this.from, this.to, this.reverse = true});
final CryptoCurrency from; final CryptoCurrency from;
final CryptoCurrency to; final CryptoCurrency to;
final bool reverse; final bool reverse;
ExchangePair({this.from, this.to, this.reverse = true});
} }

View file

@ -7,6 +7,8 @@ import 'package:cake_wallet/src/domain/exchange/trade.dart';
import 'package:cake_wallet/src/domain/exchange/exchange_provider_description.dart'; import 'package:cake_wallet/src/domain/exchange/exchange_provider_description.dart';
abstract class ExchangeProvider { abstract class ExchangeProvider {
ExchangeProvider({this.pairList});
String get title; String get title;
List<ExchangePair> pairList; List<ExchangePair> pairList;
ExchangeProviderDescription description; ExchangeProviderDescription description;

View file

@ -2,6 +2,9 @@ import 'package:cake_wallet/src/domain/common/enumerable_item.dart';
class ExchangeProviderDescription extends EnumerableItem<int> class ExchangeProviderDescription extends EnumerableItem<int>
with Serializable<int> { with Serializable<int> {
const ExchangeProviderDescription({String title, int raw})
: super(title: title, raw: raw);
static const xmrto = ExchangeProviderDescription(title: 'XMR.TO', raw: 0); static const xmrto = ExchangeProviderDescription(title: 'XMR.TO', raw: 0);
static const changeNow = static const changeNow =
ExchangeProviderDescription(title: 'ChangeNOW', raw: 1); ExchangeProviderDescription(title: 'ChangeNOW', raw: 1);
@ -16,9 +19,4 @@ class ExchangeProviderDescription extends EnumerableItem<int>
return null; return null;
} }
} }
final String title;
const ExchangeProviderDescription({this.title, int raw})
: super(title: title, raw: raw);
} }

View file

@ -1,6 +1,6 @@
class Limits { class Limits {
const Limits({this.min, this.max});
final double min; final double min;
final double max; final double max;
const Limits({this.min, this.max});
} }

View file

@ -7,6 +7,25 @@ part 'trade.g.dart';
@HiveType() @HiveType()
class Trade extends HiveObject { class Trade extends HiveObject {
Trade(
{this.id,
ExchangeProviderDescription provider,
CryptoCurrency from,
CryptoCurrency to,
TradeState state,
this.createdAt,
this.expiredAt,
this.amount,
this.inputAddress,
this.extraId,
this.outputTransaction,
this.refundAddress,
this.walletId})
: providerRaw = provider?.raw,
fromRaw = from?.raw,
toRaw = to?.raw,
stateRaw = state?.raw;
static const boxName = 'Trades'; static const boxName = 'Trades';
@HiveField(0) @HiveField(0)
@ -59,38 +78,20 @@ class Trade extends HiveObject {
static Trade fromMap(Map map) { static Trade fromMap(Map map) {
return Trade( return Trade(
id: map['id'], id: map['id'] as String,
provider: ExchangeProviderDescription.deserialize(raw: map['provider']), provider: ExchangeProviderDescription.deserialize(
from: CryptoCurrency.deserialize(raw: map['input']), raw: map['provider'] as int),
to: CryptoCurrency.deserialize(raw: map['output']), from: CryptoCurrency.deserialize(raw: map['input'] as int),
to: CryptoCurrency.deserialize(raw: map['output'] as int),
createdAt: map['date'] != null createdAt: map['date'] != null
? DateTime.fromMillisecondsSinceEpoch(map['date']) ? DateTime.fromMillisecondsSinceEpoch(map['date'] as int)
: null, : null,
amount: map['amount'], amount: map['amount'] as String,
walletId: map['wallet_id']); walletId: map['wallet_id'] as String);
} }
Trade(
{this.id,
ExchangeProviderDescription provider,
CryptoCurrency from,
CryptoCurrency to,
TradeState state,
this.createdAt,
this.expiredAt,
this.amount,
this.inputAddress,
this.extraId,
this.outputTransaction,
this.refundAddress,
this.walletId})
: providerRaw = provider?.raw,
fromRaw = from?.raw,
toRaw = to?.raw,
stateRaw = state?.raw;
Map<String, dynamic> toMap() { Map<String, dynamic> toMap() {
return { return <String, dynamic>{
'id': id, 'id': id,
'provider': provider.serialize(), 'provider': provider.serialize(),
'input': from.serialize(), 'input': from.serialize(),

View file

@ -2,11 +2,11 @@ import 'package:cake_wallet/src/domain/exchange/exchange_provider_description.da
import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/generated/i18n.dart';
class TradeNotCreatedException implements Exception { class TradeNotCreatedException implements Exception {
TradeNotCreatedException(this.provider, {this.description = ''});
ExchangeProviderDescription provider; ExchangeProviderDescription provider;
String description; String description;
TradeNotCreatedException(this.provider, {this.description = ''});
@override @override
String toString() { String toString() {
var text = provider != null var text = provider != null

View file

@ -2,12 +2,12 @@ import 'package:cake_wallet/src/domain/exchange/exchange_provider_description.da
import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/generated/i18n.dart';
class TradeNotFoundException implements Exception { class TradeNotFoundException implements Exception {
TradeNotFoundException(this.tradeId, {this.provider, this.description = ''});
String tradeId; String tradeId;
ExchangeProviderDescription provider; ExchangeProviderDescription provider;
String description; String description;
TradeNotFoundException(this.tradeId, {this.provider, this.description = ''});
@override @override
String toString() { String toString() {
var text = tradeId != null && provider != null var text = tradeId != null && provider != null

View file

@ -2,6 +2,12 @@ import 'package:flutter/foundation.dart';
import 'package:cake_wallet/src/domain/common/enumerable_item.dart'; import 'package:cake_wallet/src/domain/common/enumerable_item.dart';
class TradeState extends EnumerableItem<String> with Serializable<String> { class TradeState extends EnumerableItem<String> with Serializable<String> {
const TradeState({@required String raw, @required String title})
: super(raw: raw, title: title);
@override
bool operator ==(Object other) => other is TradeState && other.raw == raw;
static const pending = TradeState(raw: 'pending', title: 'Pending'); static const pending = TradeState(raw: 'pending', title: 'Pending');
static const confirming = TradeState(raw: 'confirming', title: 'Confirming'); static const confirming = TradeState(raw: 'confirming', title: 'Confirming');
static const trading = TradeState(raw: 'trading', title: 'Trading'); static const trading = TradeState(raw: 'trading', title: 'Trading');
@ -58,11 +64,6 @@ class TradeState extends EnumerableItem<String> with Serializable<String> {
} }
} }
const TradeState({@required String raw, @required String title})
: super(raw: raw, title: title);
operator ==(o) => o is TradeState && o.raw == raw;
@override @override
int get hashCode => raw.hashCode ^ title.hashCode; int get hashCode => raw.hashCode ^ title.hashCode;
} }

View file

@ -14,6 +14,12 @@ import 'package:cake_wallet/src/domain/exchange/exchange_provider_description.da
import 'package:cake_wallet/src/domain/exchange/trade_not_found_exeption.dart'; import 'package:cake_wallet/src/domain/exchange/trade_not_found_exeption.dart';
class XMRTOExchangeProvider extends ExchangeProvider { class XMRTOExchangeProvider extends ExchangeProvider {
XMRTOExchangeProvider()
: super(pairList: [
ExchangePair(
from: CryptoCurrency.xmr, to: CryptoCurrency.btc, reverse: false)
]);
static const userAgent = 'CakeWallet/XMR iOS'; static const userAgent = 'CakeWallet/XMR iOS';
static const originalApiUri = 'https://xmr.to/api/v2/xmr2btc'; static const originalApiUri = 'https://xmr.to/api/v2/xmr2btc';
static const proxyApiUri = 'https://xmrproxy.net/api/v2/xmr2btc'; static const proxyApiUri = 'https://xmrproxy.net/api/v2/xmr2btc';
@ -35,18 +41,16 @@ class XMRTOExchangeProvider extends ExchangeProvider {
return _apiUri; return _apiUri;
} }
@override
String get title => 'XMR.TO'; String get title => 'XMR.TO';
@override
ExchangeProviderDescription get description => ExchangeProviderDescription get description =>
ExchangeProviderDescription.xmrto; ExchangeProviderDescription.xmrto;
List<ExchangePair> pairList = [
ExchangePair(
from: CryptoCurrency.xmr, to: CryptoCurrency.btc, reverse: false)
];
double _rate = 0; double _rate = 0;
@override
Future<Limits> fetchLimits({CryptoCurrency from, CryptoCurrency to}) async { Future<Limits> fetchLimits({CryptoCurrency from, CryptoCurrency to}) async {
final url = await getApiUri() + _orderParameterUriSufix; final url = await getApiUri() + _orderParameterUriSufix;
final response = await get(url); final response = await get(url);
@ -55,13 +59,14 @@ class XMRTOExchangeProvider extends ExchangeProvider {
return Limits(min: 0, max: 0); return Limits(min: 0, max: 0);
} }
final responseJSON = json.decode(response.body); final responseJSON = json.decode(response.body) as Map<String, dynamic>;
final double min = responseJSON['lower_limit']; final min = responseJSON['lower_limit'] as double;
final double max = responseJSON['upper_limit']; final max = responseJSON['upper_limit'] as double;
return Limits(min: min, max: max); return Limits(min: min, max: max);
} }
@override
Future<Trade> createTrade({TradeRequest request}) async { Future<Trade> createTrade({TradeRequest request}) async {
final _request = request as XMRTOTradeRequest; final _request = request as XMRTOTradeRequest;
final url = await getApiUri() + _orderCreateUriSufix; final url = await getApiUri() + _orderCreateUriSufix;
@ -74,16 +79,17 @@ class XMRTOExchangeProvider extends ExchangeProvider {
if (response.statusCode != 201) { if (response.statusCode != 201) {
if (response.statusCode == 400) { if (response.statusCode == 400) {
final responseJSON = json.decode(response.body); final responseJSON = json.decode(response.body) as Map<String, dynamic>;
throw TradeNotCreatedException(description, final error = responseJSON['error_msg'] as String;
description: responseJSON['error_msg']);
throw TradeNotCreatedException(description, description: error);
} }
throw TradeNotCreatedException(description); throw TradeNotCreatedException(description);
} }
final responseJSON = json.decode(response.body); final responseJSON = json.decode(response.body) as Map<String, dynamic>;
final uuid = responseJSON["uuid"]; final uuid = responseJSON["uuid"] as String;
return Trade( return Trade(
id: uuid, id: uuid,
@ -95,6 +101,7 @@ class XMRTOExchangeProvider extends ExchangeProvider {
createdAt: DateTime.now()); createdAt: DateTime.now());
} }
@override
Future<Trade> findTradeById({@required String id}) async { Future<Trade> findTradeById({@required String id}) async {
const headers = { const headers = {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
@ -106,8 +113,9 @@ class XMRTOExchangeProvider extends ExchangeProvider {
if (response.statusCode != 200) { if (response.statusCode != 200) {
if (response.statusCode == 400) { if (response.statusCode == 400) {
final responseJSON = json.decode(response.body); final responseJSON = json.decode(response.body) as Map<String, dynamic>;
final error = responseJSON['error_msg']; final error = responseJSON['error_msg'] as String;
throw TradeNotFoundException(id, throw TradeNotFoundException(id,
provider: description, description: error); provider: description, description: error);
} }
@ -115,14 +123,14 @@ class XMRTOExchangeProvider extends ExchangeProvider {
throw TradeNotFoundException(id, provider: description); throw TradeNotFoundException(id, provider: description);
} }
final responseJSON = json.decode(response.body); final responseJSON = json.decode(response.body) as Map<String, dynamic>;
final address = responseJSON['xmr_receiving_integrated_address']; final address = responseJSON['xmr_receiving_integrated_address'] as String;
final paymentId = responseJSON['xmr_required_payment_id_short']; final paymentId = responseJSON['xmr_required_payment_id_short'] as String;
final amount = responseJSON['xmr_amount_total'].toString(); final amount = responseJSON['xmr_amount_total'].toString();
final stateRaw = responseJSON['state']; final stateRaw = responseJSON['state'] as String;
final expiredAtRaw = responseJSON['expires_at']; final expiredAtRaw = responseJSON['expires_at'] as String;
final expiredAt = DateTime.parse(expiredAtRaw).toLocal(); final expiredAt = DateTime.parse(expiredAtRaw).toLocal();
final outputTransaction = responseJSON['btc_transaction_id']; final outputTransaction = responseJSON['btc_transaction_id'] as String;
final state = TradeState.deserialize(raw: stateRaw); final state = TradeState.deserialize(raw: stateRaw);
return Trade( return Trade(
@ -138,6 +146,7 @@ class XMRTOExchangeProvider extends ExchangeProvider {
outputTransaction: outputTransaction); outputTransaction: outputTransaction);
} }
@override
Future<double> calculateAmount( Future<double> calculateAmount(
{CryptoCurrency from, CryptoCurrency to, double amount}) async { {CryptoCurrency from, CryptoCurrency to, double amount}) async {
if (from != CryptoCurrency.xmr && to != CryptoCurrency.btc) { if (from != CryptoCurrency.xmr && to != CryptoCurrency.btc) {
@ -158,9 +167,10 @@ class XMRTOExchangeProvider extends ExchangeProvider {
final url = await getApiUri() + _orderParameterUriSufix; final url = await getApiUri() + _orderParameterUriSufix;
final response = final response =
await get(url, headers: {'Content-Type': 'application/json'}); await get(url, headers: {'Content-Type': 'application/json'});
final responseJSON = json.decode(response.body); final responseJSON = json.decode(response.body) as Map<String, dynamic>;
double btcprice = responseJSON['price']; final btcprice = responseJSON['price'] as double;
double price = 1 / btcprice; final price = 1 / btcprice;
return price; return price;
} catch (e) { } catch (e) {
print(e.toString()); print(e.toString());

View file

@ -3,16 +3,16 @@ import 'package:cake_wallet/src/domain/common/crypto_currency.dart';
import 'package:cake_wallet/src/domain/exchange/trade_request.dart'; import 'package:cake_wallet/src/domain/exchange/trade_request.dart';
class XMRTOTradeRequest extends TradeRequest { class XMRTOTradeRequest extends TradeRequest {
final CryptoCurrency from;
final CryptoCurrency to;
final String amount;
final String address;
final String refundAddress;
XMRTOTradeRequest( XMRTOTradeRequest(
{@required this.from, {@required this.from,
@required this.to, @required this.to,
@required this.amount, @required this.amount,
@required this.address, @required this.address,
@required this.refundAddress}); @required this.refundAddress});
final CryptoCurrency from;
final CryptoCurrency to;
final String amount;
final String address;
final String refundAddress;
} }

View file

@ -1,16 +1,16 @@
import 'package:cw_monero/structs/account_row.dart'; import 'package:cw_monero/structs/account_row.dart';
class Account { class Account {
final int id;
final String label;
Account({this.id, this.label}); Account({this.id, this.label});
Account.fromMap(Map map) Account.fromMap(Map map)
: this.id = map['id'] == null ? 0 : int.parse(map['id']), : this.id = map['id'] == null ? 0 : int.parse(map['id'] as String),
this.label = map['label'] ?? ''; this.label = (map['label'] ?? '') as String;
Account.fromRow(AccountRow row) Account.fromRow(AccountRow row)
: this.id = row.getId(), : this.id = row.getId(),
this.label = row.getLabel(); this.label = row.getLabel();
final int id;
final String label;
} }

View file

@ -1,20 +1,20 @@
import 'package:rxdart/rxdart.dart'; import 'package:rxdart/rxdart.dart';
import 'package:cw_monero/account_list.dart' as accountListAPI; import 'package:cw_monero/account_list.dart' as account_list;
import 'package:cake_wallet/src/domain/monero/account.dart'; import 'package:cake_wallet/src/domain/monero/account.dart';
class AccountList { class AccountList {
get accounts => _accounts.stream;
BehaviorSubject<List<Account>> _accounts;
bool _isRefreshing;
bool _isUpdating;
AccountList() { AccountList() {
_isRefreshing = false; _isRefreshing = false;
_isUpdating = false; _isUpdating = false;
_accounts = BehaviorSubject<List<Account>>(); _accounts = BehaviorSubject<List<Account>>();
} }
Observable<List<Account>> get accounts => _accounts.stream;
BehaviorSubject<List<Account>> _accounts;
bool _isRefreshing;
bool _isUpdating;
Future update() async { Future update() async {
if (_isUpdating) { if (_isUpdating) {
return; return;
@ -22,46 +22,47 @@ class AccountList {
try { try {
_isUpdating = true; _isUpdating = true;
await refresh(); refresh();
final accounts = getAll(); final accounts = getAll();
_accounts.add(accounts); _accounts.add(accounts);
_isUpdating = false; _isUpdating = false;
} catch (e) { } catch (e) {
_isUpdating = false; _isUpdating = false;
throw e; rethrow;
} }
} }
List<Account> getAll() { List<Account> getAll() {
return accountListAPI return account_list
.getAllAccount() .getAllAccount()
.map((accountRow) => Account.fromRow(accountRow)) .map((accountRow) => Account.fromRow(accountRow))
.toList(); .toList();
} }
Future addAccount({String label}) async { Future addAccount({String label}) async {
await accountListAPI.addAccount(label: label); await account_list.addAccount(label: label);
await update(); await update();
} }
Future setLabelSubaddress({int accountIndex, String label}) async { Future setLabelSubaddress({int accountIndex, String label}) async {
await accountListAPI.setLabelForAccount(accountIndex: accountIndex, label: label); await account_list.setLabelForAccount(
accountIndex: accountIndex, label: label);
await update(); await update();
} }
refresh() { void refresh() {
if (_isRefreshing) { if (_isRefreshing) {
return; return;
} }
try { try {
_isRefreshing = true; _isRefreshing = true;
accountListAPI.refreshAccounts(); account_list.refreshAccounts();
_isRefreshing = false; _isRefreshing = false;
} catch (e) { } catch (e) {
_isRefreshing = false; _isRefreshing = false;
print(e); print(e);
throw e; rethrow;
} }
} }
} }

View file

@ -77,7 +77,8 @@ int getHeigthByDate({DateTime date}) {
if (endHeight <= 0) { if (endHeight <= 0) {
endHeight = dates.values.toList()[dates.length - 1]; endHeight = dates.values.toList()[dates.length - 1];
final preLastDate = dateFormat.parse(dates.keys.elementAt(dates.keys.length - 2)); final preLastDate =
dateFormat.parse(dates.keys.elementAt(dates.keys.length - 2));
preLastYear = preLastDate.year; preLastYear = preLastDate.year;
preLastMonth = preLastDate.month; preLastMonth = preLastDate.month;
} else { } else {
@ -90,12 +91,12 @@ int getHeigthByDate({DateTime date}) {
preLastYear -= 1; preLastYear -= 1;
} }
var startRaw = '$preLastYear' + '-' + '$preLastMonth'; final startRaw = '$preLastYear' + '-' + '$preLastMonth';
var startHeight = dates[startRaw]; final startHeight = dates[startRaw];
var diff = endHeight - startHeight; final diff = endHeight - startHeight;
var heightPerDay = diff / 30; final heightPerDay = diff / 30;
var daysHeight = date.day * heightPerDay.round(); final daysHeight = date.day * heightPerDay.round();
var height = endHeight + daysHeight; final height = endHeight + daysHeight;
return height; return height;
} }

View file

@ -2,8 +2,8 @@ import 'package:flutter/foundation.dart';
import 'package:cake_wallet/src/domain/common/balance.dart'; import 'package:cake_wallet/src/domain/common/balance.dart';
class MoneroBalance extends Balance { class MoneroBalance extends Balance {
MoneroBalance({@required this.fullBalance, @required this.unlockedBalance});
final String fullBalance; final String fullBalance;
final String unlockedBalance; final String unlockedBalance;
MoneroBalance({@required this.fullBalance, @required this.unlockedBalance});
} }

View file

@ -3,11 +3,11 @@ import 'package:cake_wallet/src/domain/common/transaction_priority.dart';
class MoneroTransactionCreationCredentials class MoneroTransactionCreationCredentials
extends TransactionCreationCredentials { extends TransactionCreationCredentials {
MoneroTransactionCreationCredentials(
{this.address, this.paymentId, this.priority, this.amount});
final String address; final String address;
final String paymentId; final String paymentId;
final String amount; final String amount;
final TransactionPriority priority; final TransactionPriority priority;
MoneroTransactionCreationCredentials(
{this.address, this.paymentId, this.priority, this.amount});
} }

View file

@ -1,26 +1,29 @@
import 'dart:core'; import 'dart:core';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:rxdart/rxdart.dart'; import 'package:rxdart/rxdart.dart';
import 'package:cw_monero/transaction_history.dart' as moneroTransactionHistory; import 'package:cw_monero/transaction_history.dart'
as monero_transaction_history;
import 'package:cake_wallet/src/domain/common/transaction_history.dart'; import 'package:cake_wallet/src/domain/common/transaction_history.dart';
import 'package:cake_wallet/src/domain/common/transaction_info.dart'; import 'package:cake_wallet/src/domain/common/transaction_info.dart';
List<TransactionInfo> _getAllTransactions(_) => moneroTransactionHistory List<TransactionInfo> _getAllTransactions(dynamic _) => monero_transaction_history
.getAllTransations() .getAllTransations()
.map((row) => TransactionInfo.fromRow(row)) .map((row) => TransactionInfo.fromRow(row))
.toList(); .toList();
class MoneroTransactionHistory extends TransactionHistory { class MoneroTransactionHistory extends TransactionHistory {
get transactions => _transactions.stream; MoneroTransactionHistory()
BehaviorSubject<List<TransactionInfo>> _transactions; : _transactions = BehaviorSubject<List<TransactionInfo>>.seeded([]);
@override
Observable<List<TransactionInfo>> get transactions => _transactions.stream;
final BehaviorSubject<List<TransactionInfo>> _transactions;
bool _isUpdating = false; bool _isUpdating = false;
bool _isRefreshing = false; bool _isRefreshing = false;
bool _needToCheckForRefresh = false; bool _needToCheckForRefresh = false;
MoneroTransactionHistory() @override
: _transactions = BehaviorSubject<List<TransactionInfo>>.seeded([]);
Future update() async { Future update() async {
if (_isUpdating) { if (_isUpdating) {
return; return;
@ -38,15 +41,18 @@ class MoneroTransactionHistory extends TransactionHistory {
} catch (e) { } catch (e) {
_isUpdating = false; _isUpdating = false;
print(e); print(e);
throw e; rethrow;
} }
} }
@override
Future<List<TransactionInfo>> getAll({bool force = false}) async => Future<List<TransactionInfo>> getAll({bool force = false}) async =>
_getAllTransactions(null); _getAllTransactions(null);
Future<int> count() async => moneroTransactionHistory.countOfTransactions(); @override
Future<int> count() async => monero_transaction_history.countOfTransactions();
@override
Future refresh() async { Future refresh() async {
if (_isRefreshing) { if (_isRefreshing) {
return; return;
@ -54,12 +60,12 @@ class MoneroTransactionHistory extends TransactionHistory {
try { try {
_isRefreshing = true; _isRefreshing = true;
moneroTransactionHistory.refreshTransactions(); monero_transaction_history.refreshTransactions();
_isRefreshing = false; _isRefreshing = false;
} on PlatformException catch (e) { } on PlatformException catch (e) {
_isRefreshing = false; _isRefreshing = false;
print(e); print(e);
throw e; rethrow;
} }
} }
} }

View file

@ -3,8 +3,8 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:hive/hive.dart'; import 'package:hive/hive.dart';
import 'package:rxdart/rxdart.dart'; import 'package:rxdart/rxdart.dart';
import 'package:cw_monero/wallet.dart' as moneroWallet; import 'package:cw_monero/wallet.dart' as monero_wallet;
import 'package:cw_monero/transaction_history.dart' as transactionHistory; import 'package:cw_monero/transaction_history.dart' as transaction_history;
import 'package:cake_wallet/src/domain/common/wallet_info.dart'; import 'package:cake_wallet/src/domain/common/wallet_info.dart';
import 'package:cake_wallet/src/domain/common/wallet.dart'; import 'package:cake_wallet/src/domain/common/wallet.dart';
import 'package:cake_wallet/src/domain/common/sync_status.dart'; import 'package:cake_wallet/src/domain/common/sync_status.dart';
@ -23,9 +23,25 @@ import 'package:cake_wallet/src/domain/monero/subaddress.dart';
import 'package:cake_wallet/src/domain/common/balance.dart'; import 'package:cake_wallet/src/domain/common/balance.dart';
import 'package:cake_wallet/src/domain/monero/monero_balance.dart'; import 'package:cake_wallet/src/domain/monero/monero_balance.dart';
const monero_block_size = 1000; const moneroBlockSize = 1000;
class MoneroWallet extends Wallet { class MoneroWallet extends Wallet {
MoneroWallet({this.walletInfoSource, this.walletInfo}) {
_cachedBlockchainHeight = 0;
_isSaving = false;
_lastSaveTime = 0;
_lastRefreshTime = 0;
_refreshHeight = 0;
_lastSyncHeight = 0;
_name = BehaviorSubject<String>();
_address = BehaviorSubject<String>();
_syncStatus = BehaviorSubject<SyncStatus>();
_onBalanceChange = BehaviorSubject<MoneroBalance>();
_account = BehaviorSubject<Account>()..add(Account(id: 0));
_subaddress = BehaviorSubject<Subaddress>();
setListeners();
}
static Future<MoneroWallet> createdWallet( static Future<MoneroWallet> createdWallet(
{Box<WalletInfo> walletInfoSource, {Box<WalletInfo> walletInfoSource,
String name, String name,
@ -48,8 +64,8 @@ class MoneroWallet extends Wallet {
static Future<MoneroWallet> load( static Future<MoneroWallet> load(
Box<WalletInfo> walletInfoSource, String name, WalletType type) async { Box<WalletInfo> walletInfoSource, String name, WalletType type) async {
final id = walletTypeToString(type).toLowerCase() + '_' + name; final id = walletTypeToString(type).toLowerCase() + '_' + name;
final walletInfo = final walletInfo = walletInfoSource.values
walletInfoSource.values.firstWhere((info) => info.id == id, orElse: () => null); .firstWhere((info) => info.id == id, orElse: () => null);
return await configured( return await configured(
walletInfoSource: walletInfoSource, walletInfo: walletInfo); walletInfoSource: walletInfoSource, walletInfo: walletInfo);
} }
@ -61,29 +77,44 @@ class MoneroWallet extends Wallet {
walletInfoSource: walletInfoSource, walletInfo: walletInfo); walletInfoSource: walletInfoSource, walletInfo: walletInfo);
if (walletInfo.isRecovery) { if (walletInfo.isRecovery) {
await wallet.setRecoveringFromSeed(); wallet.setRecoveringFromSeed();
if (walletInfo.restoreHeight != null) { if (walletInfo.restoreHeight != null) {
await wallet.setRefreshFromBlockHeight( wallet.setRefreshFromBlockHeight(height: walletInfo.restoreHeight);
height: walletInfo.restoreHeight);
} }
} }
return wallet; return wallet;
} }
@override
String get address => _address.value;
@override
String get name => _name.value;
@override
WalletType getType() => WalletType.monero; WalletType getType() => WalletType.monero;
bool get isRecovery => walletInfo.isRecovery;
@override
Observable<SyncStatus> get syncStatus => _syncStatus.stream; Observable<SyncStatus> get syncStatus => _syncStatus.stream;
@override
Observable<Balance> get onBalanceChange => _onBalanceChange.stream; Observable<Balance> get onBalanceChange => _onBalanceChange.stream;
Observable<Account> get onAccountChange => _account.stream;
@override
Observable<String> get onNameChange => _name.stream; Observable<String> get onNameChange => _name.stream;
@override
Observable<String> get onAddressChange => _address.stream; Observable<String> get onAddressChange => _address.stream;
Observable<Account> get onAccountChange => _account.stream;
Observable<Subaddress> get subaddress => _subaddress.stream; Observable<Subaddress> get subaddress => _subaddress.stream;
bool get isRecovery => walletInfo.isRecovery;
Account get account => _account.value; Account get account => _account.value;
String get address => _address.value;
String get name => _name.value;
Box<WalletInfo> walletInfoSource; Box<WalletInfo> walletInfoSource;
WalletInfo walletInfo; WalletInfo walletInfo;
@ -105,27 +136,11 @@ class MoneroWallet extends Wallet {
SubaddressList _cachedSubaddressList; SubaddressList _cachedSubaddressList;
AccountList _cachedAccountList; AccountList _cachedAccountList;
MoneroWallet( @override
{this.walletInfoSource, this.walletInfo}) {
_cachedBlockchainHeight = 0;
_isSaving = false;
_lastSaveTime = 0;
_lastRefreshTime = 0;
_refreshHeight = 0;
_lastSyncHeight = 0;
_name = BehaviorSubject<String>();
_address = BehaviorSubject<String>();
_syncStatus = BehaviorSubject<SyncStatus>();
_onBalanceChange = BehaviorSubject<MoneroBalance>();
_account = BehaviorSubject<Account>()..add(Account(id: 0));
_subaddress = BehaviorSubject<Subaddress>();
setListeners();
}
Future updateInfo() async { Future updateInfo() async {
_name.value = await getName(); _name.value = await getName();
final acccountList = getAccountList(); final acccountList = getAccountList();
await acccountList.refresh(); acccountList.refresh();
_account.value = acccountList.getAll().first; _account.value = acccountList.getAll().first;
final subaddressList = getSubaddress(); final subaddressList = getSubaddress();
await subaddressList.refresh( await subaddressList.refresh(
@ -135,67 +150,48 @@ class MoneroWallet extends Wallet {
_address.value = await getAddress(); _address.value = await getAddress();
} }
Future<String> getFilename() async => moneroWallet.getFilename(); @override
Future<String> getFilename() async => monero_wallet.getFilename();
@override
Future<String> getName() async => getFilename() Future<String> getName() async => getFilename()
.then((filename) => filename.split('/')) .then((filename) => filename.split('/'))
.then((splitted) => splitted.last); .then((splitted) => splitted.last);
Future<String> getAddress() async => moneroWallet.getAddress( @override
Future<String> getAddress() async => monero_wallet.getAddress(
accountIndex: _account.value.id, addressIndex: _subaddress.value.id); accountIndex: _account.value.id, addressIndex: _subaddress.value.id);
Future<String> getSeed() async => moneroWallet.getSeed(); @override
Future<String> getSeed() async => monero_wallet.getSeed();
@override
Future<String> getFullBalance() async => moneroAmountToString( Future<String> getFullBalance() async => moneroAmountToString(
amount: moneroWallet.getFullBalance(accountIndex: _account.value.id)); amount: monero_wallet.getFullBalance(accountIndex: _account.value.id));
@override
Future<String> getUnlockedBalance() async => moneroAmountToString( Future<String> getUnlockedBalance() async => moneroAmountToString(
amount: moneroWallet.getUnlockedBalance(accountIndex: _account.value.id)); amount:
monero_wallet.getUnlockedBalance(accountIndex: _account.value.id));
Future<int> getCurrentHeight() async => moneroWallet.getCurrentHeight(); @override
Future<int> getCurrentHeight() async => monero_wallet.getCurrentHeight();
Future<int> getNodeHeight() async => moneroWallet.getNodeHeight(); @override
Future<int> getNodeHeight() async => monero_wallet.getNodeHeight();
@override
Future<bool> isConnected() async => monero_wallet.isConnected();
@override
Future<Map<String, String>> getKeys() async => { Future<Map<String, String>> getKeys() async => {
'publicViewKey': moneroWallet.getPublicViewKey(), 'publicViewKey': monero_wallet.getPublicViewKey(),
'privateViewKey': moneroWallet.getSecretViewKey(), 'privateViewKey': monero_wallet.getSecretViewKey(),
'publicSpendKey': moneroWallet.getPublicSpendKey(), 'publicSpendKey': monero_wallet.getPublicSpendKey(),
'privateSpendKey': moneroWallet.getSecretSpendKey() 'privateSpendKey': monero_wallet.getSecretSpendKey()
}; };
Future close() async { @override
moneroWallet.closeListeners();
moneroWallet.closeCurrentWallet();
}
Future connectToNode(
{Node node, bool useSSL = false, bool isLightWallet = false}) async {
try {
_syncStatus.value = ConnectingSyncStatus();
await moneroWallet.setupNode(
address: node.uri,
login: node.login,
password: node.password,
useSSL: useSSL,
isLightWallet: isLightWallet);
_syncStatus.value = ConnectedSyncStatus();
} catch (e) {
_syncStatus.value = FailedSyncStatus();
print(e);
}
}
Future startSync() async {
try {
_syncStatus.value = StartingSyncStatus();
moneroWallet.startRefresh();
} on PlatformException catch (e) {
_syncStatus.value = FailedSyncStatus();
print(e);
throw e;
}
}
TransactionHistory getHistory() { TransactionHistory getHistory() {
if (_cachedTransactionHistory == null) { if (_cachedTransactionHistory == null) {
_cachedTransactionHistory = MoneroTransactionHistory(); _cachedTransactionHistory = MoneroTransactionHistory();
@ -220,6 +216,45 @@ class MoneroWallet extends Wallet {
return _cachedAccountList; return _cachedAccountList;
} }
@override
Future close() async {
monero_wallet.closeListeners();
monero_wallet.closeCurrentWallet();
await _name.close();
await _address.close();
await _subaddress.close();
}
@override
Future connectToNode(
{Node node, bool useSSL = false, bool isLightWallet = false}) async {
try {
_syncStatus.value = ConnectingSyncStatus();
await monero_wallet.setupNode(
address: node.uri,
login: node.login,
password: node.password,
useSSL: useSSL,
isLightWallet: isLightWallet);
_syncStatus.value = ConnectedSyncStatus();
} catch (e) {
_syncStatus.value = FailedSyncStatus();
print(e);
}
}
@override
Future startSync() async {
try {
_syncStatus.value = StartingSyncStatus();
monero_wallet.startRefresh();
} on PlatformException catch (e) {
_syncStatus.value = FailedSyncStatus();
print(e);
rethrow;
}
}
Future askForSave() async { Future askForSave() async {
final diff = DateTime.now().millisecondsSinceEpoch - _lastSaveTime; final diff = DateTime.now().millisecondsSinceEpoch - _lastSaveTime;
@ -238,12 +273,11 @@ class MoneroWallet extends Wallet {
return _cachedBlockchainHeight; return _cachedBlockchainHeight;
} }
@override
Future<PendingTransaction> createTransaction( Future<PendingTransaction> createTransaction(
TransactionCreationCredentials credentials) async { TransactionCreationCredentials credentials) async {
MoneroTransactionCreationCredentials _credentials = final _credentials = credentials as MoneroTransactionCreationCredentials;
credentials as MoneroTransactionCreationCredentials; final transactionDescription = await transaction_history.createTransaction(
final transactionDescription = await transactionHistory.createTransaction(
address: _credentials.address, address: _credentials.address,
paymentId: _credentials.paymentId, paymentId: _credentials.paymentId,
amount: _credentials.amount, amount: _credentials.amount,
@ -254,13 +288,19 @@ class MoneroWallet extends Wallet {
transactionDescription); transactionDescription);
} }
setRecoveringFromSeed() => @override
moneroWallet.setRecoveringFromSeed(isRecovery: true); Future rescan({int restoreHeight = 0}) async {
_syncStatus.value = StartingSyncStatus();
setRefreshFromBlockHeight(height: restoreHeight);
monero_wallet.rescanBlockchainAsync();
_syncStatus.value = StartingSyncStatus();
}
setRefreshFromBlockHeight({int height}) => void setRecoveringFromSeed() =>
moneroWallet.setRefreshFromBlockHeight(height: height); monero_wallet.setRecoveringFromSeed(isRecovery: true);
Future<bool> isConnected() async => moneroWallet.isConnected(); void setRefreshFromBlockHeight({int height}) =>
monero_wallet.setRefreshFromBlockHeight(height: height);
Future setAsRecovered() async { Future setAsRecovered() async {
walletInfo.isRecovery = false; walletInfo.isRecovery = false;
@ -285,22 +325,15 @@ class MoneroWallet extends Wallet {
Future askForUpdateTransactionHistory() async => await getHistory().update(); Future askForUpdateTransactionHistory() async => await getHistory().update();
Future rescan({int restoreHeight = 0}) async { void changeCurrentSubaddress(Subaddress subaddress) =>
_syncStatus.value = StartingSyncStatus();
setRefreshFromBlockHeight(height: restoreHeight);
moneroWallet.rescanBlockchainAsync();
_syncStatus.value = StartingSyncStatus();
}
changeCurrentSubaddress(Subaddress subaddress) =>
_subaddress.value = subaddress; _subaddress.value = subaddress;
changeAccount(Account account) { void changeAccount(Account account) {
_account.add(account); _account.add(account);
getSubaddress() getSubaddress()
.refresh(accountIndex: account.id) .refresh(accountIndex: account.id)
.then((_) => getSubaddress().getAll()) .then((dynamic _) => getSubaddress().getAll())
.then((subaddresses) => _subaddress.value = subaddresses[0]); .then((subaddresses) => _subaddress.value = subaddresses[0]);
} }
@ -311,16 +344,16 @@ class MoneroWallet extends Wallet {
try { try {
_isSaving = true; _isSaving = true;
moneroWallet.store(); await monero_wallet.store();
_isSaving = false; _isSaving = false;
} on PlatformException catch (e) { } on PlatformException catch (e) {
print(e); print(e);
_isSaving = false; _isSaving = false;
throw e; rethrow;
} }
} }
setListeners() => moneroWallet.setListeners( void setListeners() => monero_wallet.setListeners(
_onNewBlock, _onNeedToRefresh, _onNewTransaction); _onNewBlock, _onNeedToRefresh, _onNewTransaction);
Future _onNewBlock(int height) async { Future _onNewBlock(int height) async {
@ -333,13 +366,13 @@ class MoneroWallet extends Wallet {
if (isRecovery && if (isRecovery &&
(_lastSyncHeight == 0 || (_lastSyncHeight == 0 ||
(height - _lastSyncHeight) > monero_block_size)) { (height - _lastSyncHeight) > moneroBlockSize)) {
_lastSyncHeight = height; _lastSyncHeight = height;
askForUpdateBalance(); await askForUpdateBalance();
askForUpdateTransactionHistory(); await askForUpdateTransactionHistory();
} }
if (height > 0 && ((nodeHeight - height) < monero_block_size)) { if (height > 0 && ((nodeHeight - height) < moneroBlockSize)) {
_syncStatus.add(SyncedSyncStatus()); _syncStatus.add(SyncedSyncStatus());
} else { } else {
_syncStatus.add(SyncingSyncStatus(height, nodeHeight, _refreshHeight)); _syncStatus.add(SyncingSyncStatus(height, nodeHeight, _refreshHeight));
@ -363,15 +396,15 @@ class MoneroWallet extends Wallet {
return; return;
} }
askForUpdateBalance(); await askForUpdateBalance();
_syncStatus.add(SyncedSyncStatus()); _syncStatus.add(SyncedSyncStatus());
if (isRecovery) { if (isRecovery) {
askForUpdateTransactionHistory(); await askForUpdateTransactionHistory();
} }
if (isRecovery && (nodeHeight - currentHeight < monero_block_size)) { if (isRecovery && (nodeHeight - currentHeight < moneroBlockSize)) {
await setAsRecovered(); await setAsRecovered();
} }

View file

@ -3,7 +3,7 @@ import 'dart:io';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:hive/hive.dart'; import 'package:hive/hive.dart';
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
import 'package:cw_monero/wallet_manager.dart' as moneroWalletManager; import 'package:cw_monero/wallet_manager.dart' as monero_wallet_manager;
import 'package:cake_wallet/src/domain/common/wallet_info.dart'; import 'package:cake_wallet/src/domain/common/wallet_info.dart';
import 'package:cake_wallet/src/domain/common/wallet_type.dart'; import 'package:cake_wallet/src/domain/common/wallet_type.dart';
import 'package:cake_wallet/src/domain/common/wallets_manager.dart'; import 'package:cake_wallet/src/domain/common/wallets_manager.dart';
@ -24,54 +24,61 @@ Future<String> pathForWallet({String name}) async {
} }
class MoneroWalletsManager extends WalletsManager { class MoneroWalletsManager extends WalletsManager {
MoneroWalletsManager({@required this.walletInfoSource});
static const type = WalletType.monero; static const type = WalletType.monero;
Box<WalletInfo> walletInfoSource; Box<WalletInfo> walletInfoSource;
MoneroWalletsManager({@required this.walletInfoSource}); @override
Future<Wallet> create(String name, String password) async { Future<Wallet> create(String name, String password) async {
try { try {
const isRecovery = false; const isRecovery = false;
final path = await pathForWallet(name: name); final path = await pathForWallet(name: name);
await moneroWalletManager.createWallet(path: path, password: password); await monero_wallet_manager.createWallet(path: path, password: password);
final wallet = await MoneroWallet.createdWallet( final wallet = await MoneroWallet.createdWallet(
walletInfoSource: walletInfoSource, name: name, isRecovery: isRecovery) walletInfoSource: walletInfoSource,
..updateInfo(); name: name,
isRecovery: isRecovery);
await wallet.updateInfo();
return wallet; return wallet;
} catch (e) { } catch (e) {
print('MoneroWalletsManager Error: $e'); print('MoneroWalletsManager Error: $e');
throw e; rethrow;
} }
} }
@override
Future<Wallet> restoreFromSeed( Future<Wallet> restoreFromSeed(
String name, String password, String seed, int restoreHeight) async { String name, String password, String seed, int restoreHeight) async {
try { try {
const isRecovery = true; const isRecovery = true;
final path = await pathForWallet(name: name); final path = await pathForWallet(name: name);
await moneroWalletManager.restoreFromSeed( await monero_wallet_manager.restoreFromSeed(
path: path, path: path,
password: password, password: password,
seed: seed, seed: seed,
restoreHeight: restoreHeight); restoreHeight: restoreHeight);
return await MoneroWallet.createdWallet( final wallet = await MoneroWallet.createdWallet(
walletInfoSource: walletInfoSource, walletInfoSource: walletInfoSource,
name: name, name: name,
isRecovery: isRecovery, isRecovery: isRecovery,
restoreHeight: restoreHeight) restoreHeight: restoreHeight);
..updateInfo(); await wallet.updateInfo();
return wallet;
} catch (e) { } catch (e) {
print('MoneroWalletsManager Error: $e'); print('MoneroWalletsManager Error: $e');
throw e; rethrow;
} }
} }
@override
Future<Wallet> restoreFromKeys( Future<Wallet> restoreFromKeys(
String name, String name,
String password, String password,
@ -83,7 +90,7 @@ class MoneroWalletsManager extends WalletsManager {
const isRecovery = true; const isRecovery = true;
final path = await pathForWallet(name: name); final path = await pathForWallet(name: name);
await moneroWalletManager.restoreFromKeys( await monero_wallet_manager.restoreFromKeys(
path: path, path: path,
password: password, password: password,
restoreHeight: restoreHeight, restoreHeight: restoreHeight,
@ -95,40 +102,43 @@ class MoneroWalletsManager extends WalletsManager {
walletInfoSource: walletInfoSource, walletInfoSource: walletInfoSource,
name: name, name: name,
isRecovery: isRecovery, isRecovery: isRecovery,
restoreHeight: restoreHeight) restoreHeight: restoreHeight);
..updateInfo(); await wallet.updateInfo();
return wallet; return wallet;
} catch (e) { } catch (e) {
print('MoneroWalletsManager Error: $e'); print('MoneroWalletsManager Error: $e');
throw e; rethrow;
} }
} }
@override
Future<Wallet> openWallet(String name, String password) async { Future<Wallet> openWallet(String name, String password) async {
try { try {
final path = await pathForWallet(name: name); final path = await pathForWallet(name: name);
await moneroWalletManager.openWallet(path: path, password: password); monero_wallet_manager.openWallet(path: path, password: password);
final wallet = await MoneroWallet.load(walletInfoSource, name, type) final wallet = await MoneroWallet.load(walletInfoSource, name, type);
..updateInfo(); await wallet.updateInfo();
return wallet; return wallet;
} catch (e) { } catch (e) {
print('MoneroWalletsManager Error: $e'); print('MoneroWalletsManager Error: $e');
throw e; rethrow;
} }
} }
@override
Future<bool> isWalletExit(String name) async { Future<bool> isWalletExit(String name) async {
try { try {
final path = await pathForWallet(name: name); final path = await pathForWallet(name: name);
return moneroWalletManager.isWalletExist(path: path); return monero_wallet_manager.isWalletExist(path: path);
} catch (e) { } catch (e) {
print('MoneroWalletsManager Error: $e'); print('MoneroWalletsManager Error: $e');
throw e; rethrow;
} }
} }
@override
Future remove(WalletDescription wallet) async { Future remove(WalletDescription wallet) async {
final dir = await getApplicationDocumentsDirectory(); final dir = await getApplicationDocumentsDirectory();
final root = dir.path.replaceAll('app_flutter', 'files'); final root = dir.path.replaceAll('app_flutter', 'files');
@ -153,7 +163,8 @@ class MoneroWalletsManager extends WalletsManager {
final id = final id =
walletTypeToString(wallet.type).toLowerCase() + '_' + wallet.name; walletTypeToString(wallet.type).toLowerCase() + '_' + wallet.name;
final info = walletInfoSource.values.firstWhere((info) => info.id == id, orElse: () => null); final info = walletInfoSource.values
.firstWhere((info) => info.id == id, orElse: () => null);
await info?.delete(); await info?.delete();
} }

View file

@ -1,19 +1,19 @@
import 'package:cw_monero/structs/subaddress_row.dart'; import 'package:cw_monero/structs/subaddress_row.dart';
class Subaddress { class Subaddress {
final int id;
final String address;
final String label;
Subaddress({this.id, this.address, this.label}); Subaddress({this.id, this.address, this.label});
Subaddress.fromMap(Map map) Subaddress.fromMap(Map map)
: this.id = map['id'] == null ? 0 : int.parse(map['id']), : this.id = map['id'] == null ? 0 : int.parse(map['id'] as String),
this.address = map['address'] ?? '', this.address = (map['address'] ?? '') as String,
this.label = map['label'] ?? ''; this.label = (map['label'] ?? '') as String;
Subaddress.fromRow(SubaddressRow row) Subaddress.fromRow(SubaddressRow row)
: this.id = row.getId(), : this.id = row.getId(),
this.address = row.getAddress(), this.address = row.getAddress(),
this.label = row.getLabel(); this.label = row.getLabel();
final int id;
final String address;
final String label;
} }

View file

@ -1,21 +1,21 @@
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:rxdart/rxdart.dart'; import 'package:rxdart/rxdart.dart';
import 'package:cw_monero/subaddress_list.dart' as subaddressListAPI; import 'package:cw_monero/subaddress_list.dart' as subaddress_list;
import 'package:cake_wallet/src/domain/monero/subaddress.dart'; import 'package:cake_wallet/src/domain/monero/subaddress.dart';
class SubaddressList { class SubaddressList {
get subaddresses => _subaddress.stream;
BehaviorSubject<List<Subaddress>> _subaddress;
bool _isRefreshing;
bool _isUpdating;
SubaddressList() { SubaddressList() {
_isRefreshing = false; _isRefreshing = false;
_isUpdating = false; _isUpdating = false;
_subaddress = BehaviorSubject<List<Subaddress>>(); _subaddress = BehaviorSubject<List<Subaddress>>();
} }
Observable<List<Subaddress>> get subaddresses => _subaddress.stream;
BehaviorSubject<List<Subaddress>> _subaddress;
bool _isRefreshing;
bool _isUpdating;
Future update({int accountIndex}) async { Future update({int accountIndex}) async {
if (_isUpdating) { if (_isUpdating) {
return; return;
@ -29,25 +29,26 @@ class SubaddressList {
_isUpdating = false; _isUpdating = false;
} catch (e) { } catch (e) {
_isUpdating = false; _isUpdating = false;
throw e; rethrow;
} }
} }
List<Subaddress> getAll() { List<Subaddress> getAll() {
return subaddressListAPI return subaddress_list
.getAllSubaddresses() .getAllSubaddresses()
.map((subaddressRow) => Subaddress.fromRow(subaddressRow)) .map((subaddressRow) => Subaddress.fromRow(subaddressRow))
.toList(); .toList();
} }
Future addSubaddress({int accountIndex, String label}) async { Future addSubaddress({int accountIndex, String label}) async {
await subaddressListAPI.addSubaddress(accountIndex: accountIndex, label: label); await subaddress_list.addSubaddress(
accountIndex: accountIndex, label: label);
await update(accountIndex: accountIndex); await update(accountIndex: accountIndex);
} }
Future setLabelSubaddress( Future setLabelSubaddress(
{int accountIndex, int addressIndex, String label}) async { {int accountIndex, int addressIndex, String label}) async {
await subaddressListAPI.setLabelForSubaddress( await subaddress_list.setLabelForSubaddress(
accountIndex: accountIndex, addressIndex: addressIndex, label: label); accountIndex: accountIndex, addressIndex: addressIndex, label: label);
await update(); await update();
} }
@ -59,12 +60,12 @@ class SubaddressList {
try { try {
_isRefreshing = true; _isRefreshing = true;
subaddressListAPI.refreshSubaddresses(accountIndex: accountIndex); subaddress_list.refreshSubaddresses(accountIndex: accountIndex);
_isRefreshing = false; _isRefreshing = false;
} on PlatformException catch (e) { } on PlatformException catch (e) {
_isRefreshing = false; _isRefreshing = false;
print(e); print(e);
throw e; rethrow;
} }
} }
} }

View file

@ -4,6 +4,8 @@ part 'transaction_description.g.dart';
@HiveType() @HiveType()
class TransactionDescription extends HiveObject { class TransactionDescription extends HiveObject {
TransactionDescription({this.id, this.recipientAddress});
static const boxName = 'TransactionDescriptions'; static const boxName = 'TransactionDescriptions';
@HiveField(0) @HiveField(0)
@ -11,6 +13,4 @@ class TransactionDescription extends HiveObject {
@HiveField(1) @HiveField(1)
String recipientAddress; String recipientAddress;
TransactionDescription({this.id, this.recipientAddress});
} }

View file

@ -4,11 +4,11 @@ import 'package:cake_wallet/src/domain/common/secret_store_key.dart';
import 'package:cake_wallet/src/domain/common/encrypt.dart'; import 'package:cake_wallet/src/domain/common/encrypt.dart';
class UserService { class UserService {
UserService({this.sharedPreferences, this.secureStorage});
final FlutterSecureStorage secureStorage; final FlutterSecureStorage secureStorage;
final SharedPreferences sharedPreferences; final SharedPreferences sharedPreferences;
UserService({this.sharedPreferences, this.secureStorage});
Future setPassword(String password) async { Future setPassword(String password) async {
final key = generateStoreKeyFor(key: SecretStoreKey.pinCodePassword); final key = generateStoreKeyFor(key: SecretStoreKey.pinCodePassword);

View file

@ -15,21 +15,15 @@ import 'package:cake_wallet/src/domain/monero/monero_wallets_manager.dart';
import 'package:cake_wallet/src/domain/services/wallet_service.dart'; import 'package:cake_wallet/src/domain/services/wallet_service.dart';
class WalletIsExistException implements Exception { class WalletIsExistException implements Exception {
String name;
WalletIsExistException(this.name); WalletIsExistException(this.name);
String name;
@override @override
String toString() => "Wallet with name $name is already exist!"; String toString() => "Wallet with name $name is already exist!";
} }
class WalletListService { class WalletListService {
final FlutterSecureStorage secureStorage;
final WalletService walletService;
final Box<WalletInfo> walletInfoSource;
final SharedPreferences sharedPreferences;
WalletsManager walletsManager;
WalletListService( WalletListService(
{this.secureStorage, {this.secureStorage,
this.walletInfoSource, this.walletInfoSource,
@ -37,6 +31,12 @@ class WalletListService {
@required this.walletService, @required this.walletService,
@required this.sharedPreferences}); @required this.sharedPreferences});
final FlutterSecureStorage secureStorage;
final WalletService walletService;
final Box<WalletInfo> walletInfoSource;
final SharedPreferences sharedPreferences;
WalletsManager walletsManager;
Future<List<WalletDescription>> getAll() async => walletInfoSource.values Future<List<WalletDescription>> getAll() async => walletInfoSource.values
.map((info) => WalletDescription(name: info.name, type: info.type)) .map((info) => WalletDescription(name: info.name, type: info.type))
.toList(); .toList();

View file

@ -10,17 +10,40 @@ import 'package:cake_wallet/src/domain/common/pending_transaction.dart';
import 'package:cake_wallet/src/domain/common/node.dart'; import 'package:cake_wallet/src/domain/common/node.dart';
class WalletService extends Wallet { class WalletService extends Wallet {
Observable<Wallet> get onWalletChange => _onWalletChanged.stream; WalletService() {
_currentWallet = null;
walletType = WalletType.none;
_syncStatus = BehaviorSubject<SyncStatus>();
_onBalanceChange = BehaviorSubject<Balance>();
_onWalletChanged = BehaviorSubject<Wallet>();
}
@override
Observable<Balance> get onBalanceChange => _onBalanceChange.stream; Observable<Balance> get onBalanceChange => _onBalanceChange.stream;
@override
Observable<SyncStatus> get syncStatus => _syncStatus.stream; Observable<SyncStatus> get syncStatus => _syncStatus.stream;
@override
Observable<String> get onAddressChange => _currentWallet.onAddressChange; Observable<String> get onAddressChange => _currentWallet.onAddressChange;
@override
Observable<String> get onNameChange => _currentWallet.onNameChange; Observable<String> get onNameChange => _currentWallet.onNameChange;
@override
String get address => _currentWallet.address; String get address => _currentWallet.address;
@override
String get name => _currentWallet.name; String get name => _currentWallet.name;
SyncStatus get syncStatusValue => _syncStatus.value;
@override
WalletType get walletType => _currentWallet.walletType; WalletType get walletType => _currentWallet.walletType;
get currentWallet => _currentWallet; Observable<Wallet> get onWalletChange => _onWalletChanged.stream;
SyncStatus get syncStatusValue => _syncStatus.value;
Wallet get currentWallet => _currentWallet;
set currentWallet(Wallet wallet) { set currentWallet(Wallet wallet) {
_currentWallet = wallet; _currentWallet = wallet;
@ -44,55 +67,65 @@ class WalletService extends Wallet {
BehaviorSubject<SyncStatus> _syncStatus; BehaviorSubject<SyncStatus> _syncStatus;
Wallet _currentWallet; Wallet _currentWallet;
WalletService() {
_currentWallet = null;
walletType = WalletType.none;
_syncStatus = BehaviorSubject<SyncStatus>();
_onBalanceChange = BehaviorSubject<Balance>();
_onWalletChanged = BehaviorSubject<Wallet>();
}
WalletDescription description; WalletDescription description;
@override
WalletType getType() => WalletType.monero; WalletType getType() => WalletType.monero;
@override
Future<String> getFilename() => _currentWallet.getFilename(); Future<String> getFilename() => _currentWallet.getFilename();
@override
Future<String> getName() => _currentWallet.getName(); Future<String> getName() => _currentWallet.getName();
@override
Future<String> getAddress() => _currentWallet.getAddress(); Future<String> getAddress() => _currentWallet.getAddress();
@override
Future<String> getSeed() => _currentWallet.getSeed(); Future<String> getSeed() => _currentWallet.getSeed();
@override
Future<Map<String, String>> getKeys() => _currentWallet.getKeys(); Future<Map<String, String>> getKeys() => _currentWallet.getKeys();
@override
Future<String> getFullBalance() => _currentWallet.getFullBalance(); Future<String> getFullBalance() => _currentWallet.getFullBalance();
@override
Future<String> getUnlockedBalance() => _currentWallet.getUnlockedBalance(); Future<String> getUnlockedBalance() => _currentWallet.getUnlockedBalance();
@override
Future<int> getCurrentHeight() => _currentWallet.getCurrentHeight(); Future<int> getCurrentHeight() => _currentWallet.getCurrentHeight();
@override
Future<int> getNodeHeight() => _currentWallet.getNodeHeight(); Future<int> getNodeHeight() => _currentWallet.getNodeHeight();
@override
Future<bool> isConnected() => _currentWallet.isConnected(); Future<bool> isConnected() => _currentWallet.isConnected();
@override
Future close() => _currentWallet.close(); Future close() => _currentWallet.close();
Future connectToNode({Node node, bool useSSL = false, bool isLightWallet = false}) => @override
Future connectToNode(
{Node node, bool useSSL = false, bool isLightWallet = false}) =>
_currentWallet.connectToNode( _currentWallet.connectToNode(
node: node, node: node, useSSL: useSSL, isLightWallet: isLightWallet);
useSSL: useSSL,
isLightWallet: isLightWallet);
@override
Future startSync() => _currentWallet.startSync(); Future startSync() => _currentWallet.startSync();
@override
TransactionHistory getHistory() => _currentWallet.getHistory(); TransactionHistory getHistory() => _currentWallet.getHistory();
@override
Future<PendingTransaction> createTransaction( Future<PendingTransaction> createTransaction(
TransactionCreationCredentials credentials) => TransactionCreationCredentials credentials) =>
_currentWallet.createTransaction(credentials); _currentWallet.createTransaction(credentials);
@override
Future updateInfo() async => _currentWallet.updateInfo(); Future updateInfo() async => _currentWallet.updateInfo();
Future rescan({int restoreHeight = 0}) async => _currentWallet.rescan(restoreHeight: restoreHeight); @override
Future rescan({int restoreHeight = 0}) async =>
_currentWallet.rescan(restoreHeight: restoreHeight);
} }

View file

@ -1,6 +1,7 @@
import 'dart:async'; import 'dart:async';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:mobx/mobx.dart'; import 'package:mobx/mobx.dart';
import 'package:cake_wallet/src/domain/common/node.dart';
import 'package:cake_wallet/src/domain/common/sync_status.dart'; import 'package:cake_wallet/src/domain/common/sync_status.dart';
import 'package:cake_wallet/src/domain/services/wallet_service.dart'; import 'package:cake_wallet/src/domain/services/wallet_service.dart';
import 'package:cake_wallet/src/start_updating_price.dart'; import 'package:cake_wallet/src/start_updating_price.dart';
@ -11,7 +12,12 @@ import 'package:cake_wallet/src/stores/price/price_store.dart';
import 'package:cake_wallet/src/stores/authentication/authentication_store.dart'; import 'package:cake_wallet/src/stores/authentication/authentication_store.dart';
import 'package:cake_wallet/src/stores/login/login_store.dart'; import 'package:cake_wallet/src/stores/login/login_store.dart';
setReactions( Timer _reconnectionTimer;
ReactionDisposer _connectToNodeDisposer;
ReactionDisposer _onSyncStatusChangeDisposer;
ReactionDisposer _onCurrentWalletChangeDisposer;
void setReactions(
{@required SettingsStore settingsStore, {@required SettingsStore settingsStore,
@required PriceStore priceStore, @required PriceStore priceStore,
@required SyncStore syncStore, @required SyncStore syncStore,
@ -36,29 +42,44 @@ setReactions(
}); });
} }
connectToNode({SettingsStore settingsStore, WalletStore walletStore}) => void connectToNode({SettingsStore settingsStore, WalletStore walletStore}) {
reaction((_) => settingsStore.node, _connectToNodeDisposer?.call();
(node) async => await walletStore.connectToNode(node: node));
onSyncStatusChange( _connectToNodeDisposer = reaction((_) => settingsStore.node,
{SyncStore syncStore, (Node node) async => await walletStore.connectToNode(node: node));
WalletStore walletStore, }
SettingsStore settingsStore}) =>
reaction((_) => syncStore.status, (status) async {
if (status is ConnectedSyncStatus) {
await walletStore.startSync();
}
// Reconnect to the node if the app is not started sync after 30 seconds void onCurrentWalletChange(
if (status is StartingSyncStatus) { {WalletStore walletStore,
await startReconnectionObserver( SettingsStore settingsStore,
syncStore: syncStore, walletStore: walletStore); PriceStore priceStore}) {
} _onCurrentWalletChangeDisposer?.call();
});
Timer _reconnectionTimer; reaction((_) => walletStore.name, (String _) {
walletStore.connectToNode(node: settingsStore.node);
startUpdatingPrice(settingsStore: settingsStore, priceStore: priceStore);
});
}
startReconnectionObserver({SyncStore syncStore, WalletStore walletStore}) { void onSyncStatusChange(
{SyncStore syncStore,
WalletStore walletStore,
SettingsStore settingsStore}) {
_onSyncStatusChangeDisposer?.call();
reaction((_) => syncStore.status, (SyncStatus status) async {
if (status is ConnectedSyncStatus) {
await walletStore.startSync();
}
// Reconnect to the node if the app is not started sync after 30 seconds
if (status is StartingSyncStatus) {
startReconnectionObserver(syncStore: syncStore, walletStore: walletStore);
}
});
}
void startReconnectionObserver({SyncStore syncStore, WalletStore walletStore}) {
if (_reconnectionTimer != null) { if (_reconnectionTimer != null) {
_reconnectionTimer.cancel(); _reconnectionTimer.cancel();
} }
@ -75,12 +96,3 @@ startReconnectionObserver({SyncStore syncStore, WalletStore walletStore}) {
} }
}); });
} }
onCurrentWalletChange(
{WalletStore walletStore,
SettingsStore settingsStore,
PriceStore priceStore}) =>
reaction((_) => walletStore.name, (_) {
walletStore.connectToNode(node: settingsStore.node);
startUpdatingPrice(settingsStore: settingsStore, priceStore: priceStore);
});

View file

@ -11,6 +11,7 @@ import 'package:cake_wallet/src/stores/wallet/wallet_store.dart';
import 'package:cake_wallet/src/screens/base_page.dart'; import 'package:cake_wallet/src/screens/base_page.dart';
class AccountListPage extends BasePage { class AccountListPage extends BasePage {
@override
String get title => S.current.accounts; String get title => S.current.accounts;
@override @override
@ -21,8 +22,7 @@ class AccountListPage extends BasePage {
width: 28.0, width: 28.0,
height: 28.0, height: 28.0,
decoration: BoxDecoration( decoration: BoxDecoration(
shape: BoxShape.circle, shape: BoxShape.circle, color: Theme.of(context).selectedRowColor),
color: Theme.of(context).selectedRowColor),
child: Stack( child: Stack(
alignment: Alignment.center, alignment: Alignment.center,
children: <Widget>[ children: <Widget>[
@ -35,7 +35,7 @@ class AccountListPage extends BasePage {
onPressed: () async { onPressed: () async {
await Navigator.of(context) await Navigator.of(context)
.pushNamed(Routes.accountCreation); .pushNamed(Routes.accountCreation);
await accountListStore.updateAccountList(); accountListStore.updateAccountList();
}, },
child: Offstage()), child: Offstage()),
) )
@ -53,13 +53,10 @@ class AccountListPage extends BasePage {
return Container( return Container(
padding: EdgeInsets.only(top: 10, bottom: 20), padding: EdgeInsets.only(top: 10, bottom: 20),
child: Observer( child: Observer(builder: (_) {
builder: (_) { final accounts = accountListStore.accounts;
final accounts = accountListStore.accounts; return ListView.builder(
return ListView.builder( itemCount: accounts == null ? 0 : accounts.length,
itemCount: accounts == null
? 0
: accounts.length,
itemBuilder: (BuildContext context, int index) { itemBuilder: (BuildContext context, int index) {
final account = accounts[index]; final account = accounts[index];
@ -78,7 +75,10 @@ class AccountListPage extends BasePage {
account.label, account.label,
style: TextStyle( style: TextStyle(
fontSize: 16.0, fontSize: 16.0,
color: Theme.of(context).primaryTextTheme.headline.color), color: Theme.of(context)
.primaryTextTheme
.headline
.color),
), ),
onTap: () { onTap: () {
if (isCurrent) { if (isCurrent) {
@ -102,8 +102,9 @@ class AccountListPage extends BasePage {
color: Colors.blue, color: Colors.blue,
icon: Icons.edit, icon: Icons.edit,
onTap: () async { onTap: () async {
await Navigator.of(context) await Navigator.of(context).pushNamed(
.pushNamed(Routes.accountCreation, arguments: account); Routes.accountCreation,
arguments: account);
// await accountListStore.updateAccountList().then((_) { // await accountListStore.updateAccountList().then((_) {
// if (isCurrent) walletStore.setAccount(accountListStore.accounts[index]); // if (isCurrent) walletStore.setAccount(accountListStore.accounts[index]);
// }); // });
@ -113,8 +114,7 @@ class AccountListPage extends BasePage {
); );
}); });
}); });
} }),
),
); );
} }
} }

View file

@ -10,22 +10,24 @@ import 'package:cake_wallet/src/domain/monero/account.dart';
import 'package:cake_wallet/palette.dart'; import 'package:cake_wallet/palette.dart';
class AccountPage extends BasePage { class AccountPage extends BasePage {
String get title => 'Account'; AccountPage({this.account});
final Account account; final Account account;
AccountPage({this.account}); @override
String get title => 'Account';
@override @override
Widget body(BuildContext context) => AccountForm(account); Widget body(BuildContext context) => AccountForm(account);
} }
class AccountForm extends StatefulWidget { class AccountForm extends StatefulWidget {
final Account account;
AccountForm(this.account); AccountForm(this.account);
final Account account;
@override @override
createState() => AccountFormState(); AccountFormState createState() => AccountFormState();
} }
class AccountFormState extends State<AccountForm> { class AccountFormState extends State<AccountForm> {
@ -61,8 +63,8 @@ class AccountFormState extends State<AccountForm> {
hintStyle: TextStyle(color: Theme.of(context).hintColor), hintStyle: TextStyle(color: Theme.of(context).hintColor),
hintText: S.of(context).account, hintText: S.of(context).account,
focusedBorder: UnderlineInputBorder( focusedBorder: UnderlineInputBorder(
borderSide: BorderSide( borderSide:
color: Palette.cakeGreen, width: 2.0)), BorderSide(color: Palette.cakeGreen, width: 2.0)),
enabledBorder: UnderlineInputBorder( enabledBorder: UnderlineInputBorder(
borderSide: BorderSide( borderSide: BorderSide(
color: Theme.of(context).focusColor, width: 1.0))), color: Theme.of(context).focusColor, width: 1.0))),
@ -88,7 +90,7 @@ class AccountFormState extends State<AccountForm> {
await accountListStore.addAccount( await accountListStore.addAccount(
label: _textController.text); label: _textController.text);
} }
Navigator.pop(context, _textController.text); Navigator.of(context).pop(_textController.text);
}, },
text: text:
widget.account != null ? 'Rename' : S.of(context).add, widget.account != null ? 'Rename' : S.of(context).add,

View file

@ -12,13 +12,18 @@ import 'package:cake_wallet/src/stores/address_book/address_book_store.dart';
import 'package:cake_wallet/src/screens/base_page.dart'; import 'package:cake_wallet/src/screens/base_page.dart';
class AddressBookPage extends BasePage { class AddressBookPage extends BasePage {
bool get isModalBackButton => true; AddressBookPage({this.isEditable = true});
String get title => S.current.address_book;
AppBarStyle get appBarStyle => AppBarStyle.withShadow;
final bool isEditable; final bool isEditable;
AddressBookPage({this.isEditable = true}); @override
bool get isModalBackButton => true;
@override
String get title => S.current.address_book;
@override
AppBarStyle get appBarStyle => AppBarStyle.withShadow;
@override @override
Widget trailing(BuildContext context) { Widget trailing(BuildContext context) {
@ -32,8 +37,7 @@ class AddressBookPage extends BasePage {
width: 28.0, width: 28.0,
height: 28.0, height: 28.0,
decoration: BoxDecoration( decoration: BoxDecoration(
shape: BoxShape.circle, shape: BoxShape.circle, color: Theme.of(context).selectedRowColor),
color: Theme.of(context).selectedRowColor),
child: Stack( child: Stack(
alignment: Alignment.center, alignment: Alignment.center,
children: <Widget>[ children: <Widget>[
@ -79,16 +83,17 @@ class AddressBookPage extends BasePage {
return; return;
} }
bool isCopied = await showNameAndAddressDialog(context, contact.name, contact.address); final isCopied = await showNameAndAddressDialog(
context, contact.name, contact.address);
if (isCopied) { if (isCopied) {
Clipboard.setData(ClipboardData(text: contact.address)); await Clipboard.setData(
ClipboardData(text: contact.address));
Scaffold.of(context).showSnackBar( Scaffold.of(context).showSnackBar(
SnackBar( SnackBar(
content: content: Text('Copied to Clipboard'),
Text('Copied to Clipboard'),
backgroundColor: Colors.green, backgroundColor: Colors.green,
duration: duration: Duration(milliseconds: 1500),
Duration(milliseconds: 1500),
), ),
); );
} }
@ -117,48 +122,51 @@ class AddressBookPage extends BasePage {
), ),
); );
return !isEditable ? content return !isEditable
: Slidable( ? content
key: Key('1'),// Key(contact.id.toString()), : Slidable(
actionPane: SlidableDrawerActionPane(), key: Key('${contact.key}'),
child: content, actionPane: SlidableDrawerActionPane(),
secondaryActions: <Widget>[ child: content,
IconSlideAction( secondaryActions: <Widget>[
caption: 'Edit', IconSlideAction(
color: Colors.blue, caption: 'Edit',
icon: Icons.edit, color: Colors.blue,
onTap: () async { icon: Icons.edit,
await Navigator.of(context) onTap: () async {
.pushNamed(Routes.addressBookAddContact, arguments: contact); await Navigator.of(context).pushNamed(
await addressBookStore.updateContactList(); Routes.addressBookAddContact,
}, arguments: contact);
),
IconSlideAction(
caption: 'Delete',
color: Colors.red,
icon: CupertinoIcons.delete,
onTap: () async {
await showAlertDialog(context).then((isDelete) async{
if (isDelete != null && isDelete) {
await addressBookStore.delete(contact: contact);
await addressBookStore.updateContactList(); await addressBookStore.updateContactList();
} },
}); ),
}, IconSlideAction(
), caption: 'Delete',
], color: Colors.red,
dismissal: SlidableDismissal( icon: CupertinoIcons.delete,
child: SlidableDrawerDismissal(), onTap: () async {
onDismissed: (actionType) async { await showAlertDialog(context)
await addressBookStore.delete(contact: contact); .then((isDelete) async {
await addressBookStore.updateContactList(); if (isDelete != null && isDelete) {
}, await addressBookStore.delete(
onWillDismiss: (actionType) async { contact: contact);
return await showAlertDialog(context); await addressBookStore.updateContactList();
}, }
), });
},
); ),
],
dismissal: SlidableDismissal(
child: SlidableDrawerDismissal(),
onDismissed: (actionType) async {
await addressBookStore.delete(contact: contact);
await addressBookStore.updateContactList();
},
onWillDismiss: (actionType) async {
return await showAlertDialog(context);
},
),
);
}), }),
)); ));
} }
@ -220,42 +228,40 @@ class AddressBookPage extends BasePage {
), ),
actions: <Widget>[ actions: <Widget>[
FlatButton( FlatButton(
onPressed: () => onPressed: () => Navigator.of(context).pop( false),
Navigator.pop(context, false),
child: const Text('Cancel')), child: const Text('Cancel')),
FlatButton( FlatButton(
onPressed: () => onPressed: () => Navigator.of(context).pop(true),
Navigator.pop(context, true),
child: const Text('Remove')), child: const Text('Remove')),
], ],
); );
}); });
} }
showNameAndAddressDialog(BuildContext context, String name, String address) async { Future<bool> showNameAndAddressDialog(
BuildContext context, String name, String address) async {
return await showDialog( return await showDialog(
context: context, context: context,
builder: (BuildContext context) { builder: (BuildContext context) {
return AlertDialog( return AlertDialog(
title: Text( title: Text(
name, name,
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: TextStyle(fontWeight: FontWeight.bold), style: TextStyle(fontWeight: FontWeight.bold),
), ),
content: Text( content: Text(
address, address,
textAlign: TextAlign.center, textAlign: TextAlign.center,
), ),
actions: <Widget>[ actions: <Widget>[
FlatButton( FlatButton(
onPressed: () => Navigator.of(context).pop(false), onPressed: () => Navigator.of(context).pop(false),
child: Text('Cancel')), child: Text('Cancel')),
FlatButton( FlatButton(
onPressed: () => Navigator.of(context).pop(true), onPressed: () => Navigator.of(context).pop(true),
child: Text('Copy')) child: Text('Copy'))
], ],
); );
} });
);
} }
} }

View file

@ -12,22 +12,24 @@ import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
import 'package:cake_wallet/palette.dart'; import 'package:cake_wallet/palette.dart';
class ContactPage extends BasePage { class ContactPage extends BasePage {
String get title => S.current.contact; ContactPage({this.contact});
final Contact contact; final Contact contact;
ContactPage({this.contact}); @override
String get title => S.current.contact;
@override @override
Widget body(BuildContext context) => ContactForm(contact); Widget body(BuildContext context) => ContactForm(contact);
} }
class ContactForm extends StatefulWidget { class ContactForm extends StatefulWidget {
final Contact contact;
ContactForm(this.contact); ContactForm(this.contact);
final Contact contact;
@override @override
createState() => ContactFormState(); State<ContactForm> createState() => ContactFormState();
} }
class ContactFormState extends State<ContactForm> { class ContactFormState extends State<ContactForm> {
@ -59,10 +61,11 @@ class ContactFormState extends State<ContactForm> {
super.dispose(); super.dispose();
} }
_setCurrencyType(BuildContext context) async { Future<void> _setCurrencyType(BuildContext context) async {
String currencyType = CryptoCurrency.all[0].toString(); var currencyType = CryptoCurrency.all[0].toString();
CryptoCurrency selectedCurrency = CryptoCurrency.all[0]; var selectedCurrency = CryptoCurrency.all[0];
await showDialog(
await showDialog<void>(
context: context, context: context,
builder: (BuildContext context) { builder: (BuildContext context) {
return AlertDialog( return AlertDialog(
@ -127,8 +130,8 @@ class ContactFormState extends State<ContactForm> {
hintStyle: TextStyle(color: Theme.of(context).hintColor), hintStyle: TextStyle(color: Theme.of(context).hintColor),
hintText: S.of(context).contact_name, hintText: S.of(context).contact_name,
focusedBorder: UnderlineInputBorder( focusedBorder: UnderlineInputBorder(
borderSide: BorderSide( borderSide:
color: Palette.cakeGreen, width: 2.0)), BorderSide(color: Palette.cakeGreen, width: 2.0)),
enabledBorder: UnderlineInputBorder( enabledBorder: UnderlineInputBorder(
borderSide: BorderSide( borderSide: BorderSide(
color: Theme.of(context).focusColor, width: 1.0))), color: Theme.of(context).focusColor, width: 1.0))),
@ -153,8 +156,7 @@ class ContactFormState extends State<ContactForm> {
decoration: InputDecoration( decoration: InputDecoration(
focusedBorder: UnderlineInputBorder( focusedBorder: UnderlineInputBorder(
borderSide: BorderSide( borderSide: BorderSide(
color: Palette.cakeGreen, color: Palette.cakeGreen, width: 2.0)),
width: 2.0)),
enabledBorder: UnderlineInputBorder( enabledBorder: UnderlineInputBorder(
borderSide: BorderSide( borderSide: BorderSide(
color: Theme.of(context).focusColor, color: Theme.of(context).focusColor,
@ -215,13 +217,15 @@ class ContactFormState extends State<ContactForm> {
} else { } else {
widget.contact.name = _contactNameController.text; widget.contact.name = _contactNameController.text;
widget.contact.address = _addressController.text; widget.contact.address = _addressController.text;
widget.contact.updateCryptoCurrency(currency: _selectectCrypto); widget.contact
.updateCryptoCurrency(currency: _selectectCrypto);
await addressBookStore.update(contact: widget.contact); await addressBookStore.update(
contact: widget.contact);
} }
Navigator.pop(context); Navigator.pop(context);
} catch (e) { } catch (e) {
await showDialog( await showDialog<void>(
context: context, context: context,
builder: (BuildContext context) { builder: (BuildContext context) {
return AlertDialog( return AlertDialog(

View file

@ -7,13 +7,14 @@ import 'package:cake_wallet/src/stores/auth/auth_state.dart';
import 'package:cake_wallet/src/stores/auth/auth_store.dart'; import 'package:cake_wallet/src/stores/auth/auth_store.dart';
import 'package:cake_wallet/src/screens/pin_code/pin_code.dart'; import 'package:cake_wallet/src/screens/pin_code/pin_code.dart';
typedef OnAuthenticationFinished = void Function(bool, AuthPageState);
class AuthPage extends StatefulWidget { class AuthPage extends StatefulWidget {
final Function(bool, AuthPageState) onAuthenticationFinished;
final bool closable;
AuthPage({this.onAuthenticationFinished, this.closable = true}); AuthPage({this.onAuthenticationFinished, this.closable = true});
final OnAuthenticationFinished onAuthenticationFinished;
final bool closable;
@override @override
AuthPageState createState() => AuthPageState(); AuthPageState createState() => AuthPageState();
} }
@ -33,7 +34,7 @@ class AuthPageState extends State<AuthPage> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
final authStore = Provider.of<AuthStore>(context); final authStore = Provider.of<AuthStore>(context);
reaction((_) => authStore.state, (state) { reaction((_) => authStore.state, (AuthState state) {
if (state is AuthenticatedSuccessfully) { if (state is AuthenticatedSuccessfully) {
WidgetsBinding.instance.addPostFrameCallback((_) { WidgetsBinding.instance.addPostFrameCallback((_) {
if (widget.onAuthenticationFinished != null) { if (widget.onAuthenticationFinished != null) {
@ -60,7 +61,24 @@ class AuthPageState extends State<AuthPage> {
}); });
} }
if (state is AuthenticationFailure || state is AuthenticationBanned) { if (state is AuthenticationFailure) {
WidgetsBinding.instance.addPostFrameCallback((_) {
_pinCodeKey.currentState.clear();
_key.currentState.hideCurrentSnackBar();
_key.currentState.showSnackBar(
SnackBar(
content: Text(S.of(context).failed_authentication(state.error)),
backgroundColor: Colors.red,
),
);
if (widget.onAuthenticationFinished != null) {
widget.onAuthenticationFinished(false, this);
}
});
}
if (state is AuthenticationBanned) {
WidgetsBinding.instance.addPostFrameCallback((_) { WidgetsBinding.instance.addPostFrameCallback((_) {
_pinCodeKey.currentState.clear(); _pinCodeKey.currentState.clear();
_key.currentState.hideCurrentSnackBar(); _key.currentState.hideCurrentSnackBar();

View file

@ -28,7 +28,7 @@ abstract class BasePage extends StatelessWidget {
return null; return null;
} }
ThemeChanger _themeChanger = Provider.of<ThemeChanger>(context); final _themeChanger = Provider.of<ThemeChanger>(context);
Image _closeButton, _backButton; Image _closeButton, _backButton;
if (_themeChanger.getTheme() == Themes.darkTheme) { if (_themeChanger.getTheme() == Themes.darkTheme) {
@ -70,7 +70,7 @@ abstract class BasePage extends StatelessWidget {
Widget floatingActionButton(BuildContext context) => null; Widget floatingActionButton(BuildContext context) => null;
Widget appBar(BuildContext context) { ObstructingPreferredSizeWidget appBar(BuildContext context) {
final _themeChanger = Provider.of<ThemeChanger>(context); final _themeChanger = Provider.of<ThemeChanger>(context);
final _isDarkTheme = _themeChanger.getTheme() == Themes.darkTheme; final _isDarkTheme = _themeChanger.getTheme() == Themes.darkTheme;

View file

@ -1,6 +1,6 @@
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:date_range_picker/date_range_picker.dart' as DateRagePicker; import 'package:date_range_picker/date_range_picker.dart' as date_rage_picker;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:flutter_mobx/flutter_mobx.dart';
@ -86,24 +86,19 @@ class DashboardPage extends BasePage {
child: Image.asset('assets/images/exchange_icon.png', child: Image.asset('assets/images/exchange_icon.png',
color: Colors.white, height: 26, width: 22), color: Colors.white, height: 26, width: 22),
backgroundColor: Palette.floatingActionButton, backgroundColor: Palette.floatingActionButton,
onPressed: () async { onPressed: () async => await Navigator.of(context, rootNavigator: true)
final actionListStore = Provider.of<ActionListStore>(context); .pushNamed(Routes.exchange));
await Navigator.of(context, rootNavigator: true)
.pushNamed(Routes.exchange);
actionListStore.updateTradeList();
});
void _presentWalletMenu(BuildContext bodyContext) { void _presentWalletMenu(BuildContext bodyContext) {
final walletMenu = WalletMenu(bodyContext); final walletMenu = WalletMenu(bodyContext);
showDialog( showDialog<void>(
builder: (_) => Picker( builder: (_) => Picker(
items: walletMenu.items, items: walletMenu.items,
selectedAtIndex: -1, selectedAtIndex: -1,
title: S.of(bodyContext).wallet_menu, title: S.of(bodyContext).wallet_menu,
pickerHeight: 510, pickerHeight: 510,
onItemSelected: (item) => onItemSelected: (String item) =>
walletMenu.action(walletMenu.items.indexOf(item))), walletMenu.action(walletMenu.items.indexOf(item))),
context: bodyContext); context: bodyContext);
} }
@ -136,8 +131,9 @@ class DashboardPageBodyState extends State<DashboardPageBody> {
return Observer( return Observer(
key: _listObserverKey, key: _listObserverKey,
builder: (_) { builder: (_) {
final items = final items = actionListStore.items == null
actionListStore.items == null ? [] : actionListStore.items; ? <String>[]
: actionListStore.items;
final itemsCount = items.length + 2; final itemsCount = items.length + 2;
return ListView.builder( return ListView.builder(
@ -225,19 +221,17 @@ class DashboardPageBodyState extends State<DashboardPageBody> {
builder: (_) { builder: (_) {
final savedDisplayMode = final savedDisplayMode =
settingsStore.balanceDisplayMode; settingsStore.balanceDisplayMode;
final displayMode = final displayMode = balanceStore
balanceStore.isReversing .isReversing
? (savedDisplayMode == ? (savedDisplayMode ==
BalanceDisplayMode BalanceDisplayMode
.availableBalance .availableBalance
? BalanceDisplayMode ? BalanceDisplayMode.fullBalance
.fullBalance : BalanceDisplayMode
: BalanceDisplayMode .availableBalance)
.availableBalance) : savedDisplayMode;
: savedDisplayMode;
var title = displayMode.toString();
return Text(title, return Text(displayMode.toString(),
style: TextStyle( style: TextStyle(
color: Palette.violet, color: Palette.violet,
fontSize: 16)); fontSize: 16));
@ -248,16 +242,15 @@ class DashboardPageBodyState extends State<DashboardPageBody> {
final savedDisplayMode = final savedDisplayMode =
settingsStore.balanceDisplayMode; settingsStore.balanceDisplayMode;
var balance = '---'; var balance = '---';
final displayMode = final displayMode = balanceStore
balanceStore.isReversing .isReversing
? (savedDisplayMode == ? (savedDisplayMode ==
BalanceDisplayMode BalanceDisplayMode
.availableBalance .availableBalance
? BalanceDisplayMode ? BalanceDisplayMode.fullBalance
.fullBalance : BalanceDisplayMode
: BalanceDisplayMode .availableBalance)
.availableBalance) : savedDisplayMode;
: savedDisplayMode;
if (displayMode == if (displayMode ==
BalanceDisplayMode.availableBalance) { BalanceDisplayMode.availableBalance) {
@ -499,7 +492,7 @@ class DashboardPageBodyState extends State<DashboardPageBody> {
onSelected: (item) async { onSelected: (item) async {
if (item == 2) { if (item == 2) {
final List<DateTime> picked = final List<DateTime> picked =
await DateRagePicker.showDatePicker( await date_rage_picker.showDatePicker(
context: context, context: context,
initialFirstDate: DateTime.now() initialFirstDate: DateTime.now()
.subtract(Duration(days: 1)), .subtract(Duration(days: 1)),

View file

@ -4,17 +4,19 @@ import 'package:intl/intl.dart';
import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/generated/i18n.dart';
class DateSectionRaw extends StatelessWidget { class DateSectionRaw extends StatelessWidget {
DateSectionRaw({this.date});
static final dateSectionDateFormat = DateFormat("d MMM"); static final dateSectionDateFormat = DateFormat("d MMM");
static final nowDate = DateTime.now(); static final nowDate = DateTime.now();
final DateTime date; final DateTime date;
DateSectionRaw({this.date});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final diffDays = date.difference(nowDate).inDays; final diffDays = date.difference(nowDate).inDays;
final isToday = nowDate.day == date.day && nowDate.month == date.month && nowDate.year == date.year; final isToday = nowDate.day == date.day &&
nowDate.month == date.month &&
nowDate.year == date.year;
var title = ""; var title = "";
if (isToday) { if (isToday) {

View file

@ -4,13 +4,6 @@ import 'package:cake_wallet/src/domain/common/crypto_currency.dart';
import 'package:cake_wallet/src/domain/exchange/exchange_provider_description.dart'; import 'package:cake_wallet/src/domain/exchange/exchange_provider_description.dart';
class TradeRow extends StatelessWidget { class TradeRow extends StatelessWidget {
final VoidCallback onTap;
final ExchangeProviderDescription provider;
final CryptoCurrency from;
final CryptoCurrency to;
final String createdAtFormattedDate;
final String formattedAmount;
TradeRow( TradeRow(
{this.provider, {this.provider,
this.from, this.from,
@ -19,6 +12,13 @@ class TradeRow extends StatelessWidget {
this.formattedAmount, this.formattedAmount,
@required this.onTap}); @required this.onTap});
final VoidCallback onTap;
final ExchangeProviderDescription provider;
final CryptoCurrency from;
final CryptoCurrency to;
final String createdAtFormattedDate;
final String formattedAmount;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final amountCrypto = provider == ExchangeProviderDescription.xmrto final amountCrypto = provider == ExchangeProviderDescription.xmrto
@ -48,7 +48,10 @@ class TradeRow extends StatelessWidget {
Text('${from.toString()}${to.toString()}', Text('${from.toString()}${to.toString()}',
style: TextStyle( style: TextStyle(
fontSize: 16, fontSize: 16,
color: Theme.of(context).primaryTextTheme.subhead.color)), color: Theme.of(context)
.primaryTextTheme
.subhead
.color)),
formattedAmount != null formattedAmount != null
? Text(formattedAmount + ' ' + amountCrypto, ? Text(formattedAmount + ' ' + amountCrypto,
style: const TextStyle( style: const TextStyle(

View file

@ -4,13 +4,6 @@ import 'package:cake_wallet/src/domain/common/transaction_direction.dart';
import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/generated/i18n.dart';
class TransactionRow extends StatelessWidget { class TransactionRow extends StatelessWidget {
final VoidCallback onTap;
final TransactionDirection direction;
final String formattedDate;
final String formattedAmount;
final String formattedFiatAmount;
final bool isPending;
TransactionRow( TransactionRow(
{this.direction, {this.direction,
this.formattedDate, this.formattedDate,
@ -19,6 +12,13 @@ class TransactionRow extends StatelessWidget {
this.isPending, this.isPending,
@required this.onTap}); @required this.onTap});
final VoidCallback onTap;
final TransactionDirection direction;
final String formattedDate;
final String formattedAmount;
final String formattedFiatAmount;
final bool isPending;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return InkWell( return InkWell(
@ -53,7 +53,10 @@ class TransactionRow extends StatelessWidget {
(isPending ? S.of(context).pending : ''), (isPending ? S.of(context).pending : ''),
style: TextStyle( style: TextStyle(
fontSize: 16, fontSize: 16,
color: Theme.of(context).primaryTextTheme.subhead.color)), color: Theme.of(context)
.primaryTextTheme
.subhead
.color)),
Text(formattedAmount, Text(formattedAmount,
style: const TextStyle( style: const TextStyle(
fontSize: 16, color: Palette.purpleBlue)) fontSize: 16, color: Palette.purpleBlue))

View file

@ -1,10 +1,13 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:cake_wallet/routes.dart'; import 'package:cake_wallet/routes.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:cake_wallet/src/stores/wallet/wallet_store.dart';
import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/stores/wallet/wallet_store.dart';
import 'package:cake_wallet/src/screens/auth/auth_page.dart';
class WalletMenu { class WalletMenu {
WalletMenu(this.context);
final List<String> items = [ final List<String> items = [
S.current.reconnect, S.current.reconnect,
S.current.rescan, S.current.rescan,
@ -14,9 +17,8 @@ class WalletMenu {
S.current.accounts, S.current.accounts,
S.current.address_book_menu S.current.address_book_menu
]; ];
final BuildContext context;
WalletMenu(this.context); final BuildContext context;
void action(int index) { void action(int index) {
switch (index) { switch (index) {
@ -32,7 +34,7 @@ class WalletMenu {
break; break;
case 3: case 3:
Navigator.of(context).pushNamed(Routes.auth, Navigator.of(context).pushNamed(Routes.auth,
arguments: (isAuthenticatedSuccessfully, auth) => arguments: (bool isAuthenticatedSuccessfully, AuthPageState auth) =>
isAuthenticatedSuccessfully isAuthenticatedSuccessfully
? Navigator.of(auth.context).popAndPushNamed(Routes.seed) ? Navigator.of(auth.context).popAndPushNamed(Routes.seed)
: null); : null);
@ -40,7 +42,7 @@ class WalletMenu {
break; break;
case 4: case 4:
Navigator.of(context).pushNamed(Routes.auth, Navigator.of(context).pushNamed(Routes.auth,
arguments: (isAuthenticatedSuccessfully, auth) => arguments: (bool isAuthenticatedSuccessfully, AuthPageState auth) =>
isAuthenticatedSuccessfully isAuthenticatedSuccessfully
? Navigator.of(auth.context) ? Navigator.of(auth.context)
.popAndPushNamed(Routes.showKeys) .popAndPushNamed(Routes.showKeys)
@ -57,10 +59,10 @@ class WalletMenu {
} }
} }
Future _presentReconnectAlert(BuildContext context) async { Future<void> _presentReconnectAlert(BuildContext context) async {
final walletStore = Provider.of<WalletStore>(context); final walletStore = Provider.of<WalletStore>(context);
await showDialog( await showDialog<void>(
context: context, context: context,
builder: (BuildContext context) { builder: (BuildContext context) {
return AlertDialog( return AlertDialog(

View file

@ -8,10 +8,10 @@ import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/widgets/primary_button.dart'; import 'package:cake_wallet/src/widgets/primary_button.dart';
class DisclaimerPage extends BasePage { class DisclaimerPage extends BasePage {
final bool isReadOnly;
DisclaimerPage({this.isReadOnly = false}); DisclaimerPage({this.isReadOnly = false});
final bool isReadOnly;
@override @override
bool get isModalBackButton => false; bool get isModalBackButton => false;
@ -23,25 +23,25 @@ class DisclaimerPage extends BasePage {
} }
class DisclaimerPageBody extends StatefulWidget { class DisclaimerPageBody extends StatefulWidget {
final bool isReadOnly;
DisclaimerPageBody({this.isReadOnly = true}); DisclaimerPageBody({this.isReadOnly = true});
final bool isReadOnly;
@override @override
createState() => DisclaimerBodyState(false); DisclaimerBodyState createState() => DisclaimerBodyState(false);
} }
class DisclaimerBodyState extends State<DisclaimerPageBody> { class DisclaimerBodyState extends State<DisclaimerPageBody> {
static const xmrto_url = 'https://xmr.to/app_static/html/tos.html'; DisclaimerBodyState(this._isAccepted);
static const changenow_url = 'https://changenow.io/terms-of-use';
bool _isAccepted; static const xmrtoUrl = 'https://xmr.to/app_static/html/tos.html';
static const changenowUrl = 'https://changenow.io/terms-of-use';
final bool _isAccepted;
bool _checked = false; bool _checked = false;
String _fileText = ''; String _fileText = '';
DisclaimerBodyState(this._isAccepted); Future<void> launchUrl(String url) async {
launchUrl(String url) async {
if (await canLaunch(url)) await launch(url); if (await canLaunch(url)) await launch(url);
} }
@ -50,8 +50,8 @@ class DisclaimerBodyState extends State<DisclaimerPageBody> {
setState(() {}); setState(() {});
} }
_showAlertDialog(BuildContext context) async { Future<void> _showAlertDialog(BuildContext context) async {
await showDialog( await showDialog<void>(
context: context, context: context,
builder: (BuildContext context) { builder: (BuildContext context) {
return AlertDialog( return AlertDialog(
@ -66,7 +66,7 @@ class DisclaimerBodyState extends State<DisclaimerPageBody> {
actions: <Widget>[ actions: <Widget>[
FlatButton( FlatButton(
onPressed: () { onPressed: () {
Navigator.pop(context); Navigator.of(context).pop();
}, },
child: Text('OK')), child: Text('OK')),
], ],
@ -74,9 +74,7 @@ class DisclaimerBodyState extends State<DisclaimerPageBody> {
}); });
} }
_afterLayout(_) { void _afterLayout(Duration _) => _showAlertDialog(context);
_showAlertDialog(context);
}
@override @override
void initState() { void initState() {
@ -87,7 +85,6 @@ class DisclaimerBodyState extends State<DisclaimerPageBody> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Column( return Column(
children: <Widget>[ children: <Widget>[
SizedBox(height: 10.0), SizedBox(height: 10.0),
@ -166,9 +163,9 @@ class DisclaimerBodyState extends State<DisclaimerPageBody> {
children: <Widget>[ children: <Widget>[
Expanded( Expanded(
child: GestureDetector( child: GestureDetector(
onTap: () => launchUrl(xmrto_url), onTap: () => launchUrl(xmrtoUrl),
child: Text( child: Text(
xmrto_url, xmrtoUrl,
textAlign: TextAlign.left, textAlign: TextAlign.left,
style: TextStyle( style: TextStyle(
color: Colors.blue, color: Colors.blue,
@ -187,17 +184,17 @@ class DisclaimerBodyState extends State<DisclaimerPageBody> {
children: <Widget>[ children: <Widget>[
Expanded( Expanded(
child: GestureDetector( child: GestureDetector(
onTap: () => launchUrl(changenow_url), onTap: () => launchUrl(changenowUrl),
child: Text( child: Text(
changenow_url, changenowUrl,
textAlign: TextAlign.left, textAlign: TextAlign.left,
style: TextStyle( style: TextStyle(
color: Colors.blue, color: Colors.blue,
fontSize: 14.0, fontSize: 14.0,
fontWeight: FontWeight.normal, fontWeight: FontWeight.normal,
decoration: TextDecoration.underline), decoration: TextDecoration.underline),
), ),
)) ))
], ],
), ),
SizedBox( SizedBox(
@ -288,8 +285,14 @@ class DisclaimerBodyState extends State<DisclaimerPageBody> {
child: PrimaryButton( child: PrimaryButton(
onPressed: _checked ? () {} : null, onPressed: _checked ? () {} : null,
text: 'Accept', text: 'Accept',
color: Theme.of(context).primaryTextTheme.button.backgroundColor, color: Theme.of(context)
borderColor: Theme.of(context).primaryTextTheme.button.decorationColor, .primaryTextTheme
.button
.backgroundColor,
borderColor: Theme.of(context)
.primaryTextTheme
.button
.decorationColor,
), ),
) )
: Offstage(), : Offstage(),

View file

@ -6,8 +6,9 @@ import 'package:provider/provider.dart';
import 'package:cake_wallet/palette.dart'; import 'package:cake_wallet/palette.dart';
import 'package:cake_wallet/routes.dart'; import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/domain/exchange/exchange_provider_description.dart';
import 'package:cake_wallet/src/domain/common/crypto_currency.dart'; import 'package:cake_wallet/src/domain/common/crypto_currency.dart';
import 'package:cake_wallet/src/domain/exchange/exchange_provider.dart';
import 'package:cake_wallet/src/domain/exchange/exchange_provider_description.dart';
import 'package:cake_wallet/src/domain/exchange/xmrto/xmrto_exchange_provider.dart'; import 'package:cake_wallet/src/domain/exchange/xmrto/xmrto_exchange_provider.dart';
import 'package:cake_wallet/src/stores/exchange/exchange_trade_state.dart'; import 'package:cake_wallet/src/stores/exchange/exchange_trade_state.dart';
import 'package:cake_wallet/src/stores/exchange/limits_state.dart'; import 'package:cake_wallet/src/stores/exchange/limits_state.dart';
@ -20,15 +21,14 @@ import 'package:cake_wallet/src/widgets/picker.dart';
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart'; import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
class ExchangePage extends BasePage { class ExchangePage extends BasePage {
@override
String get title => S.current.exchange; String get title => S.current.exchange;
@override @override
bool get isModalBackButton => true; bool get isModalBackButton => true;
final Image arrowBottomPurple = Image.asset( final Image arrowBottomPurple =
'assets/images/arrow_bottom_purple_icon.png', Image.asset('assets/images/arrow_bottom_purple_icon.png', height: 8);
height: 8,
);
@override @override
Widget middle(BuildContext context) { Widget middle(BuildContext context) {
@ -93,12 +93,12 @@ class ExchangePage extends BasePage {
final items = exchangeStore.providersForCurrentPair(); final items = exchangeStore.providersForCurrentPair();
final selectedItem = items.indexOf(exchangeStore.provider); final selectedItem = items.indexOf(exchangeStore.provider);
showDialog( showDialog<void>(
builder: (_) => Picker( builder: (_) => Picker(
items: items, items: items,
selectedAtIndex: selectedItem, selectedAtIndex: selectedItem,
title: S.of(context).change_exchange_provider, title: S.of(context).change_exchange_provider,
onItemSelected: (provider) => onItemSelected: (ExchangeProvider provider) =>
exchangeStore.changeProvider(provider: provider)), exchangeStore.changeProvider(provider: provider)),
context: context); context: context);
} }
@ -253,8 +253,9 @@ class ExchangeFormState extends State<ExchangeForm> {
builder: (_) => LoadingPrimaryButton( builder: (_) => LoadingPrimaryButton(
text: S.of(context).exchange, text: S.of(context).exchange,
onPressed: () { onPressed: () {
if (_formKey.currentState.validate()) if (_formKey.currentState.validate()) {
exchangeStore.createTrade(); exchangeStore.createTrade();
}
}, },
color: Theme.of(context) color: Theme.of(context)
.primaryTextTheme .primaryTextTheme
@ -327,34 +328,38 @@ class ExchangeFormState extends State<ExchangeForm> {
reaction( reaction(
(_) => walletStore.name, (_) => walletStore.name,
(_) => _onWalletNameChange( (String _) => _onWalletNameChange(
walletStore, store.receiveCurrency, receiveKey)); walletStore, store.receiveCurrency, receiveKey));
reaction( reaction(
(_) => walletStore.name, (_) => walletStore.name,
(_) => _onWalletNameChange( (String _) => _onWalletNameChange(
walletStore, store.depositCurrency, depositKey)); walletStore, store.depositCurrency, depositKey));
reaction((_) => store.receiveCurrency, reaction(
(currency) => _onCurrencyChange(currency, walletStore, receiveKey)); (_) => store.receiveCurrency,
(CryptoCurrency currency) =>
_onCurrencyChange(currency, walletStore, receiveKey));
reaction((_) => store.depositCurrency, reaction(
(currency) => _onCurrencyChange(currency, walletStore, depositKey)); (_) => store.depositCurrency,
(CryptoCurrency currency) =>
_onCurrencyChange(currency, walletStore, depositKey));
reaction((_) => store.depositAmount, (amount) { reaction((_) => store.depositAmount, (String amount) {
if (depositKey.currentState.amountController.text != amount) { if (depositKey.currentState.amountController.text != amount) {
depositKey.currentState.amountController.text = amount; depositKey.currentState.amountController.text = amount;
} }
}); });
reaction((_) => store.receiveAmount, (amount) { reaction((_) => store.receiveAmount, (String amount) {
if (receiveKey.currentState.amountController.text != if (receiveKey.currentState.amountController.text !=
store.receiveAmount) { store.receiveAmount) {
receiveKey.currentState.amountController.text = amount; receiveKey.currentState.amountController.text = amount;
} }
}); });
reaction((_) => store.provider, (provider) { reaction((_) => store.provider, (ExchangeProvider provider) {
final isReversed = provider is XMRTOExchangeProvider; final isReversed = provider is XMRTOExchangeProvider;
if (isReversed) { if (isReversed) {
@ -373,10 +378,10 @@ class ExchangeFormState extends State<ExchangeForm> {
receiveKey.currentState.changeIsAmountEstimated(!isReversed); receiveKey.currentState.changeIsAmountEstimated(!isReversed);
}); });
reaction((_) => store.tradeState, (state) { reaction((_) => store.tradeState, (ExchangeTradeState state) {
if (state is TradeIsCreatedFailure) { if (state is TradeIsCreatedFailure) {
WidgetsBinding.instance.addPostFrameCallback((_) { WidgetsBinding.instance.addPostFrameCallback((_) {
showDialog( showDialog<void>(
context: context, context: context,
builder: (BuildContext context) { builder: (BuildContext context) {
return AlertDialog( return AlertDialog(
@ -397,7 +402,7 @@ class ExchangeFormState extends State<ExchangeForm> {
} }
}); });
reaction((_) => store.limitsState, (state) { reaction((_) => store.limitsState, (LimitsState state) {
final isXMRTO = store.provider is XMRTOExchangeProvider; final isXMRTO = store.provider is XMRTOExchangeProvider;
String min; String min;
String max; String max;
@ -444,7 +449,7 @@ class ExchangeFormState extends State<ExchangeForm> {
} }
}); });
reaction((_) => walletStore.address, (address) { reaction((_) => walletStore.address, (String address) {
if (store.depositCurrency == CryptoCurrency.xmr) { if (store.depositCurrency == CryptoCurrency.xmr) {
depositKey.currentState.changeAddress(address: address); depositKey.currentState.changeAddress(address: address);
} }

View file

@ -7,18 +7,6 @@ import 'package:cake_wallet/src/widgets/picker.dart';
import 'package:cake_wallet/src/widgets/address_text_field.dart'; import 'package:cake_wallet/src/widgets/address_text_field.dart';
class ExchangeCard extends StatefulWidget { class ExchangeCard extends StatefulWidget {
final List<CryptoCurrency> currencies;
final Function(CryptoCurrency) onCurrencySelected;
final CryptoCurrency initialCurrency;
final String initialWalletName;
final String initialAddress;
final bool initialIsAmountEditable;
final bool initialIsAddressEditable;
final bool isAmountEstimated;
final Image imageArrow;
final FormFieldValidator<String> currencyValueValidator;
final FormFieldValidator<String> addressTextFieldValidator;
ExchangeCard( ExchangeCard(
{Key key, {Key key,
this.initialCurrency, this.initialCurrency,
@ -34,8 +22,20 @@ class ExchangeCard extends StatefulWidget {
this.addressTextFieldValidator}) this.addressTextFieldValidator})
: super(key: key); : super(key: key);
final List<CryptoCurrency> currencies;
final Function(CryptoCurrency) onCurrencySelected;
final CryptoCurrency initialCurrency;
final String initialWalletName;
final String initialAddress;
final bool initialIsAmountEditable;
final bool initialIsAddressEditable;
final bool isAmountEstimated;
final Image imageArrow;
final FormFieldValidator<String> currencyValueValidator;
final FormFieldValidator<String> addressTextFieldValidator;
@override @override
createState() => ExchangeCardState(); ExchangeCardState createState() => ExchangeCardState();
} }
class ExchangeCardState extends State<ExchangeCard> { class ExchangeCardState extends State<ExchangeCard> {
@ -102,7 +102,6 @@ class ExchangeCardState extends State<ExchangeCard> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Container( return Container(
padding: EdgeInsets.fromLTRB(22, 15, 22, 30), padding: EdgeInsets.fromLTRB(22, 15, 22, 30),
width: double.infinity, width: double.infinity,
@ -150,7 +149,10 @@ class ExchangeCardState extends State<ExchangeCard> {
style: TextStyle( style: TextStyle(
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
fontSize: 24, fontSize: 24,
color: Theme.of(context).primaryTextTheme.title.color)), color: Theme.of(context)
.primaryTextTheme
.title
.color)),
widget.imageArrow widget.imageArrow
]), ]),
_walletName != null _walletName != null
@ -173,7 +175,10 @@ class ExchangeCardState extends State<ExchangeCard> {
textAlign: TextAlign.right, textAlign: TextAlign.right,
keyboardType: TextInputType.numberWithOptions( keyboardType: TextInputType.numberWithOptions(
signed: false, decimal: false), signed: false, decimal: false),
inputFormatters: [BlacklistingTextInputFormatter(new RegExp('[\\-|\\ |\\,]'))], inputFormatters: [
BlacklistingTextInputFormatter(
RegExp('[\\-|\\ |\\,]'))
],
decoration: InputDecoration( decoration: InputDecoration(
hintStyle: TextStyle( hintStyle: TextStyle(
color: Theme.of(context).cardTheme.color, color: Theme.of(context).cardTheme.color,
@ -182,15 +187,14 @@ class ExchangeCardState extends State<ExchangeCard> {
hintText: '0.00000000', hintText: '0.00000000',
focusedBorder: UnderlineInputBorder( focusedBorder: UnderlineInputBorder(
borderSide: BorderSide( borderSide: BorderSide(
color: Palette.cakeGreen, color: Palette.cakeGreen, width: 2.0)),
width: 2.0)),
enabledBorder: UnderlineInputBorder( enabledBorder: UnderlineInputBorder(
borderSide: BorderSide( borderSide: BorderSide(
color: _isAmountEditable color: _isAmountEditable
? Palette.deepPurple ? Palette.deepPurple
: Theme.of(context).focusColor, : Theme.of(context).focusColor,
width: 1.0))), width: 1.0))),
validator: widget.currencyValueValidator), validator: widget.currencyValueValidator),
SizedBox(height: 5), SizedBox(height: 5),
SizedBox( SizedBox(
height: 15, height: 15,
@ -201,21 +205,29 @@ class ExchangeCardState extends State<ExchangeCard> {
children: <Widget>[ children: <Widget>[
_min != null _min != null
? Text( ? Text(
S.of(context).min_value(_min, _selectedCurrency.toString()), S.of(context).min_value(
_min, _selectedCurrency.toString()),
style: TextStyle( style: TextStyle(
fontSize: 10, fontSize: 10,
height: 1.2, height: 1.2,
color: Theme.of(context).primaryTextTheme.subtitle.color), color: Theme.of(context)
.primaryTextTheme
.subtitle
.color),
) )
: SizedBox(), : SizedBox(),
_min != null ? SizedBox(width: 10) : SizedBox(), _min != null ? SizedBox(width: 10) : SizedBox(),
_max != null _max != null
? Text( ? Text(
S.of(context).max_value(_max, _selectedCurrency.toString()), S.of(context).max_value(
_max, _selectedCurrency.toString()),
style: TextStyle( style: TextStyle(
fontSize: 10, fontSize: 10,
height: 1.2, height: 1.2,
color: Theme.of(context).primaryTextTheme.subtitle.color)) color: Theme.of(context)
.primaryTextTheme
.subtitle
.color))
: SizedBox(), : SizedBox(),
]), ]),
), ),
@ -248,14 +260,15 @@ class ExchangeCardState extends State<ExchangeCard> {
} }
void _presentPicker(BuildContext context) { void _presentPicker(BuildContext context) {
showDialog( showDialog<void>(
builder: (_) => Picker( builder: (_) => Picker(
items: widget.currencies, items: widget.currencies,
selectedAtIndex: widget.currencies.indexOf(_selectedCurrency), selectedAtIndex: widget.currencies.indexOf(_selectedCurrency),
title: S.of(context).change_currency, title: S.of(context).change_currency,
onItemSelected: (item) => widget.onCurrencySelected != null onItemSelected: (CryptoCurrency item) =>
? widget.onCurrencySelected(item) widget.onCurrencySelected != null
: null), ? widget.onCurrencySelected(item)
: null),
context: context); context: context);
} }
} }

View file

@ -8,11 +8,12 @@ import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/domain/exchange/trade.dart'; import 'package:cake_wallet/src/domain/exchange/trade.dart';
class ExchangeConfirmPage extends BasePage { class ExchangeConfirmPage extends BasePage {
String get title => S.current.copy_id; ExchangeConfirmPage({@required this.trade});
final Trade trade; final Trade trade;
ExchangeConfirmPage({@required this.trade}); @override
String get title => S.current.copy_id;
@override @override
Widget body(BuildContext context) { Widget body(BuildContext context) {

View file

@ -20,6 +20,7 @@ import 'package:cake_wallet/src/widgets/primary_button.dart';
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart'; import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
class ExchangeTradePage extends BasePage { class ExchangeTradePage extends BasePage {
@override
String get title => S.current.exchange; String get title => S.current.exchange;
@override @override
@ -28,7 +29,7 @@ class ExchangeTradePage extends BasePage {
class ExchangeTradeForm extends StatefulWidget { class ExchangeTradeForm extends StatefulWidget {
@override @override
createState() => ExchangeTradeState(); ExchangeTradeState createState() => ExchangeTradeState();
} }
class ExchangeTradeState extends State<ExchangeTradeForm> { class ExchangeTradeState extends State<ExchangeTradeForm> {
@ -330,10 +331,10 @@ class ExchangeTradeState extends State<ExchangeTradeForm> {
final sendStore = Provider.of<SendStore>(context); final sendStore = Provider.of<SendStore>(context);
reaction((_) => sendStore.state, (state) { reaction((_) => sendStore.state, (SendingState state) {
if (state is SendingFailed) { if (state is SendingFailed) {
WidgetsBinding.instance.addPostFrameCallback((_) { WidgetsBinding.instance.addPostFrameCallback((_) {
showDialog( showDialog<void>(
context: context, context: context,
builder: (BuildContext context) { builder: (BuildContext context) {
return AlertDialog( return AlertDialog(
@ -351,7 +352,7 @@ class ExchangeTradeState extends State<ExchangeTradeForm> {
if (state is TransactionCreatedSuccessfully) { if (state is TransactionCreatedSuccessfully) {
WidgetsBinding.instance.addPostFrameCallback((_) { WidgetsBinding.instance.addPostFrameCallback((_) {
showDialog( showDialog<void>(
context: context, context: context,
builder: (BuildContext context) { builder: (BuildContext context) {
return AlertDialog( return AlertDialog(

View file

@ -2,29 +2,29 @@ import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class CopyButton extends StatelessWidget { class CopyButton extends StatelessWidget {
const CopyButton(
{@required this.onPressed,
@required this.text,
@required this.color,
@required this.borderColor});
final VoidCallback onPressed; final VoidCallback onPressed;
final Color color; final Color color;
final Color borderColor; final Color borderColor;
final String text; final String text;
const CopyButton({
@required this.onPressed,
@required this.text,
@required this.color,
@required this.borderColor});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return ButtonTheme( return ButtonTheme(
minWidth: double.infinity, minWidth: double.infinity,
height: 44.0, height: 44.0,
child: FlatButton( child: FlatButton(
onPressed: onPressed, onPressed: onPressed,
color: color, color: color,
shape: RoundedRectangleBorder(side: BorderSide(color: borderColor), borderRadius: BorderRadius.circular(10.0)), shape: RoundedRectangleBorder(
child: Text(text, style: TextStyle(fontSize: 14.0)), side: BorderSide(color: borderColor),
) borderRadius: BorderRadius.circular(10.0)),
); child: Text(text, style: TextStyle(fontSize: 14.0)),
));
} }
} }

View file

@ -3,24 +3,24 @@ import 'dart:async';
import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/generated/i18n.dart';
class TimerWidget extends StatefulWidget { class TimerWidget extends StatefulWidget {
TimerWidget(this.expiratedAt, {this.color = Colors.black});
final DateTime expiratedAt; final DateTime expiratedAt;
final Color color; final Color color;
TimerWidget(this.expiratedAt, {this.color = Colors.black});
@override @override
createState() => TimerWidgetState(); TimerWidgetState createState() => TimerWidgetState();
} }
class TimerWidgetState extends State<TimerWidget> { class TimerWidgetState extends State<TimerWidget> {
TimerWidgetState();
int _leftSeconds; int _leftSeconds;
int _minutes; int _minutes;
int _seconds; int _seconds;
bool _isExpired; bool _isExpired;
Timer _timer; Timer _timer;
TimerWidgetState();
@override @override
void initState() { void initState() {
super.initState(); super.initState();
@ -52,7 +52,8 @@ class TimerWidgetState extends State<TimerWidget> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return _isExpired return _isExpired
? Text(S.of(context).expired, style: TextStyle(fontSize: 14.0, color: Colors.red)) ? Text(S.of(context).expired,
style: TextStyle(fontSize: 14.0, color: Colors.red))
: Text( : Text(
S.of(context).time(_minutes.toString(), _seconds.toString()), S.of(context).time(_minutes.toString(), _seconds.toString()),
style: TextStyle(fontSize: 14.0, color: widget.color), style: TextStyle(fontSize: 14.0, color: widget.color),

View file

@ -7,14 +7,16 @@ import 'package:cake_wallet/src/stores/settings/settings_store.dart';
import 'package:cake_wallet/src/screens/base_page.dart'; import 'package:cake_wallet/src/screens/base_page.dart';
class FaqPage extends BasePage { class FaqPage extends BasePage {
@override
String get title => S.current.faq; String get title => S.current.faq;
@override @override
Widget body(BuildContext context) { Widget body(BuildContext context) {
return FutureBuilder( return FutureBuilder(
builder: (context, snapshot) { builder: (context, snapshot) {
var faqItems = json.decode(snapshot.data.toString()); final faqItems = (json.decode(snapshot.data.toString())
as List<Map<String, String>>) ??
<Map<String, String>>[];
return ListView.separated( return ListView.separated(
itemBuilder: (BuildContext context, int index) { itemBuilder: (BuildContext context, int index) {
@ -22,34 +24,24 @@ class FaqPage extends BasePage {
final itemChild = faqItems[index]["answer"]; final itemChild = faqItems[index]["answer"];
return ExpansionTile( return ExpansionTile(
title: Text( title: Text(itemTitle),
itemTitle
),
children: <Widget>[ children: <Widget>[
Row( Row(
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[ children: <Widget>[
Expanded( Expanded(
child: Container( child: Container(
padding: EdgeInsets.only( padding: EdgeInsets.only(left: 15.0, right: 15.0),
left: 15.0, child: Text(itemChild),
right: 15.0 ))
),
child: Text(
itemChild,
),
)
)
], ],
) )
], ],
); );
}, },
separatorBuilder: (_, __) => Divider( separatorBuilder: (_, __) =>
color: Theme.of(context).dividerTheme.color, Divider(color: Theme.of(context).dividerTheme.color, height: 1.0),
height: 1.0, itemCount: faqItems.length,
),
itemCount: faqItems == null ? 0 : faqItems.length,
); );
}, },
future: rootBundle.loadString(getFaqPath(context)), future: rootBundle.loadString(getFaqPath(context)),
@ -86,5 +78,4 @@ class FaqPage extends BasePage {
return 'assets/faq/faq_en.json'; return 'assets/faq/faq_en.json';
} }
} }
} }

View file

@ -15,24 +15,25 @@ import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
import 'package:cake_wallet/palette.dart'; import 'package:cake_wallet/palette.dart';
class NewWalletPage extends BasePage { class NewWalletPage extends BasePage {
final WalletListService walletsService;
final WalletService walletService;
final SharedPreferences sharedPreferences;
String get title => S.current.new_wallet;
NewWalletPage( NewWalletPage(
{@required this.walletsService, {@required this.walletsService,
@required this.walletService, @required this.walletService,
@required this.sharedPreferences}); @required this.sharedPreferences});
final WalletListService walletsService;
final WalletService walletService;
final SharedPreferences sharedPreferences;
@override
String get title => S.current.new_wallet;
@override @override
Widget body(BuildContext context) => WalletNameForm(); Widget body(BuildContext context) => WalletNameForm();
} }
class WalletNameForm extends StatefulWidget { class WalletNameForm extends StatefulWidget {
@override @override
createState() => _WalletNameFormState(); _WalletNameFormState createState() => _WalletNameFormState();
} }
class _WalletNameFormState extends State<WalletNameForm> { class _WalletNameFormState extends State<WalletNameForm> {
@ -43,14 +44,14 @@ class _WalletNameFormState extends State<WalletNameForm> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
final walletCreationStore = Provider.of<WalletCreationStore>(context); final walletCreationStore = Provider.of<WalletCreationStore>(context);
reaction((_) => walletCreationStore.state, (state) { reaction((_) => walletCreationStore.state, (WalletCreationState state) {
if (state is WalletCreatedSuccessfully) { if (state is WalletCreatedSuccessfully) {
Navigator.of(context).popUntil((route) => route.isFirst); Navigator.of(context).popUntil((route) => route.isFirst);
} }
if (state is WalletCreationFailure) { if (state is WalletCreationFailure) {
WidgetsBinding.instance.addPostFrameCallback((_) { WidgetsBinding.instance.addPostFrameCallback((_) {
showDialog( showDialog<void>(
context: context, context: context,
builder: (BuildContext context) { builder: (BuildContext context) {
return AlertDialog( return AlertDialog(
@ -88,8 +89,8 @@ class _WalletNameFormState extends State<WalletNameForm> {
fontSize: 24.0, color: Theme.of(context).hintColor), fontSize: 24.0, color: Theme.of(context).hintColor),
hintText: S.of(context).wallet_name, hintText: S.of(context).wallet_name,
focusedBorder: UnderlineInputBorder( focusedBorder: UnderlineInputBorder(
borderSide: BorderSide( borderSide:
color: Palette.cakeGreen, width: 2.0)), BorderSide(color: Palette.cakeGreen, width: 2.0)),
enabledBorder: UnderlineInputBorder( enabledBorder: UnderlineInputBorder(
borderSide: BorderSide( borderSide: BorderSide(
color: Theme.of(context).focusColor, color: Theme.of(context).focusColor,

View file

@ -17,7 +17,7 @@ class NewNodePage extends BasePage {
class NewNodePageForm extends StatefulWidget { class NewNodePageForm extends StatefulWidget {
@override @override
createState() => NewNodeFormState(); NewNodeFormState createState() => NewNodeFormState();
} }
class NewNodeFormState extends State<NewNodePageForm> { class NewNodeFormState extends State<NewNodePageForm> {

View file

@ -2,18 +2,16 @@ import 'package:flutter/material.dart';
import 'package:cake_wallet/palette.dart'; import 'package:cake_wallet/palette.dart';
class NodeIndicator extends StatelessWidget { class NodeIndicator extends StatelessWidget {
final color;
NodeIndicator({this.color = Palette.red}); NodeIndicator({this.color = Palette.red});
final Color color;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Container( return Container(
width: 10.0, width: 10.0,
height: 10.0, height: 10.0,
decoration: BoxDecoration( decoration: BoxDecoration(shape: BoxShape.circle, color: color),
shape: BoxShape.circle,
color: color),
); );
} }
} }

View file

@ -13,6 +13,7 @@ import 'package:cake_wallet/src/screens/base_page.dart';
class NodeListPage extends BasePage { class NodeListPage extends BasePage {
NodeListPage(); NodeListPage();
@override
String get title => S.current.nodes; String get title => S.current.nodes;
@override @override
@ -26,7 +27,7 @@ class NodeListPage extends BasePage {
minWidth: double.minPositive, minWidth: double.minPositive,
child: FlatButton( child: FlatButton(
onPressed: () async { onPressed: () async {
await showDialog( await showDialog<void>(
context: context, context: context,
builder: (BuildContext context) { builder: (BuildContext context) {
return AlertDialog( return AlertDialog(
@ -76,10 +77,8 @@ class NodeListPage extends BasePage {
height: 28.0, height: 28.0,
child: FlatButton( child: FlatButton(
shape: CircleBorder(), shape: CircleBorder(),
onPressed: () async { onPressed: () async =>
await Navigator.of(context).pushNamed(Routes.newNode); await Navigator.of(context).pushNamed(Routes.newNode),
nodeList.update();
},
child: Offstage()), child: Offstage()),
) )
], ],
@ -94,7 +93,7 @@ class NodeListPage extends BasePage {
class NodeListPageBody extends StatefulWidget { class NodeListPageBody extends StatefulWidget {
@override @override
createState() => NodeListPageBodyState(); NodeListPageBodyState createState() => NodeListPageBodyState();
} }
class NodeListPageBodyState extends State<NodeListPageBody> { class NodeListPageBodyState extends State<NodeListPageBody> {
@ -141,7 +140,7 @@ class NodeListPageBodyState extends State<NodeListPageBody> {
switch (snapshot.connectionState) { switch (snapshot.connectionState) {
case ConnectionState.done: case ConnectionState.done:
return NodeIndicator( return NodeIndicator(
color: snapshot.data color: snapshot.data as bool
? Palette.green ? Palette.green
: Palette.red); : Palette.red);
default: default:
@ -150,7 +149,7 @@ class NodeListPageBodyState extends State<NodeListPageBody> {
}), }),
onTap: () async { onTap: () async {
if (!isCurrent) { if (!isCurrent) {
await showDialog( await showDialog<void>(
context: context, context: context,
builder: (BuildContext context) { builder: (BuildContext context) {
return AlertDialog( return AlertDialog(

View file

@ -6,19 +6,20 @@ import 'package:cake_wallet/src/stores/settings/settings_store.dart';
import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/generated/i18n.dart';
abstract class PinCodeWidget extends StatefulWidget { abstract class PinCodeWidget extends StatefulWidget {
final Function(List<int> pin, PinCodeState state) onPinCodeEntered;
final bool hasLengthSwitcher;
PinCodeWidget({Key key, this.onPinCodeEntered, this.hasLengthSwitcher}) PinCodeWidget({Key key, this.onPinCodeEntered, this.hasLengthSwitcher})
: super(key: key); : super(key: key);
final Function(List<int> pin, PinCodeState state) onPinCodeEntered;
final bool hasLengthSwitcher;
} }
class PinCode extends PinCodeWidget { class PinCode extends PinCodeWidget {
final Function(List<int> pin, PinCodeState state) onPinCodeEntered; PinCode(Function(List<int> pin, PinCodeState state) onPinCodeEntered,
final bool hasLengthSwitcher; bool hasLengthSwitcher, Key key)
: super(
PinCode(this.onPinCodeEntered, this.hasLengthSwitcher, Key key) key: key,
: super(key: key); onPinCodeEntered: onPinCodeEntered,
hasLengthSwitcher: hasLengthSwitcher);
@override @override
PinCodeState createState() => PinCodeState(); PinCodeState createState() => PinCodeState();
@ -30,27 +31,22 @@ class PinCodeState<T extends PinCodeWidget> extends State<T> {
static const fourPinLength = 4; static const fourPinLength = 4;
static final deleteIconImage = Image.asset('assets/images/delete_icon.png'); static final deleteIconImage = Image.asset('assets/images/delete_icon.png');
static final backArrowImage = Image.asset('assets/images/back_arrow.png'); static final backArrowImage = Image.asset('assets/images/back_arrow.png');
GlobalKey _gridViewKey = GlobalKey(); final _gridViewKey = GlobalKey();
int pinLength = defaultPinLength; int pinLength = defaultPinLength;
List<int> pin = List<int>.filled(defaultPinLength, null); List<int> pin = List<int>.filled(defaultPinLength, null);
String title = S.current.enter_your_pin; String title = S.current.enter_your_pin;
double _aspectRatio = 0; double _aspectRatio = 0;
void setTitle(String title) { void setTitle(String title) => setState(() => this.title = title);
setState(() => this.title = title);
}
void clear() { void clear() => setState(() => pin = List<int>.filled(pinLength, null));
setState(() => pin = List<int>.filled(pinLength, null));
}
void onPinCodeEntered(PinCodeState state) { void onPinCodeEntered(PinCodeState state) =>
widget.onPinCodeEntered(state.pin, this); widget.onPinCodeEntered(state.pin, this);
}
void changePinLength(int length) { void changePinLength(int length) {
List<int> newPin = List<int>.filled(length, null); final newPin = List<int>.filled(length, null);
setState(() { setState(() {
pinLength = length; pinLength = length;
@ -58,19 +54,23 @@ class PinCodeState<T extends PinCodeWidget> extends State<T> {
}); });
} }
setDefaultPinLength() { void setDefaultPinLength() {
final settingsStore = Provider.of<SettingsStore>(context); final settingsStore = Provider.of<SettingsStore>(context);
pinLength = settingsStore.defaultPinLength; pinLength = settingsStore.defaultPinLength;
changePinLength(pinLength); changePinLength(pinLength);
} }
getCurrentAspectRatio() { void calculateAspectRatio() {
final RenderBox renderBox = _gridViewKey.currentContext.findRenderObject(); final renderBox =
_gridViewKey.currentContext.findRenderObject() as RenderBox;
final cellWidth = renderBox.size.width / 3;
final cellHeight = renderBox.size.height / 4;
if (cellWidth > 0 && cellHeight > 0) {
_aspectRatio = cellWidth / cellHeight;
}
double cellWidth = renderBox.size.width / 3;
double cellHeight = renderBox.size.height / 4;
if (cellWidth > 0 && cellHeight > 0) _aspectRatio = cellWidth / cellHeight;
setState(() {}); setState(() {});
} }
@ -80,15 +80,13 @@ class PinCodeState<T extends PinCodeWidget> extends State<T> {
WidgetsBinding.instance.addPostFrameCallback(afterLayout); WidgetsBinding.instance.addPostFrameCallback(afterLayout);
} }
afterLayout(_) { void afterLayout(dynamic _) {
setDefaultPinLength(); setDefaultPinLength();
getCurrentAspectRatio(); calculateAspectRatio();
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) => Scaffold(body: body(context));
return Scaffold(body: body(context));
}
Widget body(BuildContext context) { Widget body(BuildContext context) {
return SafeArea( return SafeArea(
@ -196,7 +194,7 @@ class PinCodeState<T extends PinCodeWidget> extends State<T> {
} }
void _push(int num) { void _push(int num) {
if (_pinLength() >= pinLength) { if (currentPinLength() >= pinLength) {
return; return;
} }
@ -207,13 +205,15 @@ class PinCodeState<T extends PinCodeWidget> extends State<T> {
} }
} }
if (_pinLength() == pinLength) { final _currentPinLength = currentPinLength();
if (_currentPinLength == pinLength) {
onPinCodeEntered(this); onPinCodeEntered(this);
} }
} }
void _pop() { void _pop() {
if (_pinLength() == 0) { if (currentPinLength() == 0) {
return; return;
} }
@ -225,7 +225,7 @@ class PinCodeState<T extends PinCodeWidget> extends State<T> {
} }
} }
int _pinLength() { int currentPinLength() {
return pin.fold(0, (v, e) { return pin.fold(0, (v, e) {
if (e != null) { if (e != null) {
return v + 1; return v + 1;

View file

@ -10,7 +10,7 @@ class QrImage extends StatelessWidget {
Color foregroundColor = Colors.black, Color foregroundColor = Colors.black,
int version = 7, int version = 7,
int errorCorrectionLevel = QrErrorCorrectLevel.L, int errorCorrectionLevel = QrErrorCorrectLevel.L,
}) : _painter = new QrPainter(data, foregroundColor, version, errorCorrectionLevel); }) : _painter = QrPainter(data, foregroundColor, version, errorCorrectionLevel);
final QrPainter _painter; final QrPainter _painter;
final Color backgroundColor; final Color backgroundColor;
@ -18,7 +18,7 @@ class QrImage extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return new Container( return Container(
width: size, width: size,
height: size, height: size,
color: backgroundColor, color: backgroundColor,

View file

@ -3,31 +3,32 @@ import 'package:qr/qr.dart';
class QrPainter extends CustomPainter { class QrPainter extends CustomPainter {
QrPainter( QrPainter(
String data, String data,
this.color, this.color,
this.version, this.version,
this.errorCorrectionLevel, this.errorCorrectionLevel,
) : this._qr = new QrCode(version, errorCorrectionLevel) { ) : this._qr = QrCode(version, errorCorrectionLevel) {
_p.color = this.color; _p.color = this.color;
_qr.addData(data); _qr.addData(data);
_qr.make(); _qr.make();
} }
final QrCode _qr;
final _p = new Paint()..style = PaintingStyle.fill;
final int version; final int version;
final int errorCorrectionLevel; final int errorCorrectionLevel;
final Color color; final Color color;
final QrCode _qr;
final _p = Paint()..style = PaintingStyle.fill;
@override @override
void paint(Canvas canvas, Size size) { void paint(Canvas canvas, Size size) {
final squareSize = size.shortestSide / _qr.moduleCount; final squareSize = size.shortestSide / _qr.moduleCount;
for (int x = 0; x < _qr.moduleCount; x++) { for (int x = 0; x < _qr.moduleCount; x++) {
for (int y = 0; y < _qr.moduleCount; y++) { for (int y = 0; y < _qr.moduleCount; y++) {
if (_qr.isDark(y, x)) { if (_qr.isDark(y, x)) {
final squareRect = new Rect.fromLTWH(x * squareSize, y * squareSize, squareSize, squareSize); final squareRect = Rect.fromLTWH(
x * squareSize, y * squareSize, squareSize, squareSize);
canvas.drawRect(squareRect, _p); canvas.drawRect(squareRect, _p);
} }
} }
@ -37,9 +38,11 @@ class QrPainter extends CustomPainter {
@override @override
bool shouldRepaint(CustomPainter oldDelegate) { bool shouldRepaint(CustomPainter oldDelegate) {
if (oldDelegate is QrPainter) { if (oldDelegate is QrPainter) {
return this.color != oldDelegate.color || this.errorCorrectionLevel != oldDelegate.errorCorrectionLevel || return this.color != oldDelegate.color ||
this.errorCorrectionLevel != oldDelegate.errorCorrectionLevel ||
this.version != oldDelegate.version; this.version != oldDelegate.version;
} }
return false; return false;
} }
} }

View file

@ -13,7 +13,10 @@ import 'package:cake_wallet/src/screens/receive/qr_image.dart';
import 'package:cake_wallet/src/screens/base_page.dart'; import 'package:cake_wallet/src/screens/base_page.dart';
class ReceivePage extends BasePage { class ReceivePage extends BasePage {
@override
bool get isModalBackButton => true; bool get isModalBackButton => true;
@override
String get title => S.current.receive; String get title => S.current.receive;
@override @override
@ -31,7 +34,10 @@ class ReceivePage extends BasePage {
padding: EdgeInsets.all(0), padding: EdgeInsets.all(0),
onPressed: () => Share.text( onPressed: () => Share.text(
'Share address', walletStore.subaddress.address, 'text/plain'), 'Share address', walletStore.subaddress.address, 'text/plain'),
child: Icon(Icons.share, size: 30.0,)), child: Icon(
Icons.share,
size: 30.0,
)),
), ),
); );
} }
@ -43,7 +49,7 @@ class ReceivePage extends BasePage {
class ReceiveBody extends StatefulWidget { class ReceiveBody extends StatefulWidget {
@override @override
createState() => ReceiveBodyState(); ReceiveBodyState createState() => ReceiveBodyState();
} }
class ReceiveBodyState extends State<ReceiveBody> { class ReceiveBodyState extends State<ReceiveBody> {
@ -67,8 +73,9 @@ class ReceiveBodyState extends State<ReceiveBody> {
amountController.addListener(() { amountController.addListener(() {
if (_formKey.currentState.validate()) { if (_formKey.currentState.validate()) {
walletStore.onChangedAmountValue(amountController.text); walletStore.onChangedAmountValue(amountController.text);
} else } else {
walletStore.onChangedAmountValue(''); walletStore.onChangedAmountValue('');
}
}); });
return SafeArea( return SafeArea(
@ -152,7 +159,7 @@ class ReceiveBodyState extends State<ReceiveBody> {
TextInputType.numberWithOptions(decimal: true), TextInputType.numberWithOptions(decimal: true),
inputFormatters: [ inputFormatters: [
BlacklistingTextInputFormatter( BlacklistingTextInputFormatter(
new RegExp('[\\-|\\ |\\,]')) RegExp('[\\-|\\ |\\,]'))
], ],
style: TextStyle( style: TextStyle(
fontSize: 14.0, fontSize: 14.0,
@ -163,8 +170,7 @@ class ReceiveBodyState extends State<ReceiveBody> {
hintText: S.of(context).amount, hintText: S.of(context).amount,
focusedBorder: UnderlineInputBorder( focusedBorder: UnderlineInputBorder(
borderSide: BorderSide( borderSide: BorderSide(
color: Palette.cakeGreen, color: Palette.cakeGreen, width: 2.0)),
width: 2.0)),
enabledBorder: UnderlineInputBorder( enabledBorder: UnderlineInputBorder(
borderSide: BorderSide( borderSide: BorderSide(
color: Theme.of(context).focusColor, color: Theme.of(context).focusColor,

View file

@ -9,7 +9,10 @@ import 'package:cake_wallet/generated/i18n.dart';
class RestoreOptionsPage extends BasePage { class RestoreOptionsPage extends BasePage {
static const _aspectRatioImage = 2.086; static const _aspectRatioImage = 2.086;
@override
String get title => S.current.restore_restore_wallet; String get title => S.current.restore_restore_wallet;
@override
Color get backgroundColor => Palette.creamyGrey; Color get backgroundColor => Palette.creamyGrey;
final _imageSeedKeys = Image.asset('assets/images/seedKeys.png'); final _imageSeedKeys = Image.asset('assets/images/seedKeys.png');
@ -26,7 +29,8 @@ class RestoreOptionsPage extends BasePage {
Flexible( Flexible(
child: RestoreButton( child: RestoreButton(
onPressed: () { onPressed: () {
Navigator.pushNamed(context, Routes.restoreWalletOptionsFromWelcome); Navigator.pushNamed(
context, Routes.restoreWalletOptionsFromWelcome);
}, },
image: _imageSeedKeys, image: _imageSeedKeys,
aspectRatioImage: _aspectRatioImage, aspectRatioImage: _aspectRatioImage,

View file

@ -17,24 +17,25 @@ import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
import 'package:cake_wallet/palette.dart'; import 'package:cake_wallet/palette.dart';
class RestoreWalletFromKeysPage extends BasePage { class RestoreWalletFromKeysPage extends BasePage {
final WalletListService walletsService;
final WalletService walletService;
final SharedPreferences sharedPreferences;
String get title => S.current.restore_title_from_keys;
RestoreWalletFromKeysPage( RestoreWalletFromKeysPage(
{@required this.walletsService, {@required this.walletsService,
@required this.sharedPreferences, @required this.sharedPreferences,
@required this.walletService}); @required this.walletService});
final WalletListService walletsService;
final WalletService walletService;
final SharedPreferences sharedPreferences;
@override
String get title => S.current.restore_title_from_keys;
@override @override
Widget body(BuildContext context) => RestoreFromKeysFrom(); Widget body(BuildContext context) => RestoreFromKeysFrom();
} }
class RestoreFromKeysFrom extends StatefulWidget { class RestoreFromKeysFrom extends StatefulWidget {
@override @override
createState() => _RestoreFromKeysFromState(); _RestoreFromKeysFromState createState() => _RestoreFromKeysFromState();
} }
class _RestoreFromKeysFromState extends State<RestoreFromKeysFrom> { class _RestoreFromKeysFromState extends State<RestoreFromKeysFrom> {
@ -49,14 +50,14 @@ class _RestoreFromKeysFromState extends State<RestoreFromKeysFrom> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
final walletRestorationStore = Provider.of<WalletRestorationStore>(context); final walletRestorationStore = Provider.of<WalletRestorationStore>(context);
reaction((_) => walletRestorationStore.state, (state) { reaction((_) => walletRestorationStore.state, (WalletRestorationState state) {
if (state is WalletRestoredSuccessfully) { if (state is WalletRestoredSuccessfully) {
Navigator.of(context).popUntil((route) => route.isFirst); Navigator.of(context).popUntil((route) => route.isFirst);
} }
if (state is WalletRestorationFailure) { if (state is WalletRestorationFailure) {
WidgetsBinding.instance.addPostFrameCallback((_) { WidgetsBinding.instance.addPostFrameCallback((_) {
showDialog( showDialog<void>(
context: context, context: context,
builder: (BuildContext context) { builder: (BuildContext context) {
return AlertDialog( return AlertDialog(
@ -97,8 +98,7 @@ class _RestoreFromKeysFromState extends State<RestoreFromKeysFrom> {
hintText: S.of(context).restore_wallet_name, hintText: S.of(context).restore_wallet_name,
focusedBorder: UnderlineInputBorder( focusedBorder: UnderlineInputBorder(
borderSide: BorderSide( borderSide: BorderSide(
color: Palette.cakeGreen, color: Palette.cakeGreen, width: 2.0)),
width: 2.0)),
enabledBorder: UnderlineInputBorder( enabledBorder: UnderlineInputBorder(
borderSide: BorderSide( borderSide: BorderSide(
color: Theme.of(context).focusColor, color: Theme.of(context).focusColor,
@ -127,8 +127,7 @@ class _RestoreFromKeysFromState extends State<RestoreFromKeysFrom> {
hintText: S.of(context).restore_address, hintText: S.of(context).restore_address,
focusedBorder: UnderlineInputBorder( focusedBorder: UnderlineInputBorder(
borderSide: BorderSide( borderSide: BorderSide(
color: Palette.cakeGreen, color: Palette.cakeGreen, width: 2.0)),
width: 2.0)),
enabledBorder: UnderlineInputBorder( enabledBorder: UnderlineInputBorder(
borderSide: BorderSide( borderSide: BorderSide(
color: Theme.of(context).focusColor, color: Theme.of(context).focusColor,
@ -155,8 +154,7 @@ class _RestoreFromKeysFromState extends State<RestoreFromKeysFrom> {
hintText: S.of(context).restore_view_key_private, hintText: S.of(context).restore_view_key_private,
focusedBorder: UnderlineInputBorder( focusedBorder: UnderlineInputBorder(
borderSide: BorderSide( borderSide: BorderSide(
color: Palette.cakeGreen, color: Palette.cakeGreen, width: 2.0)),
width: 2.0)),
enabledBorder: UnderlineInputBorder( enabledBorder: UnderlineInputBorder(
borderSide: BorderSide( borderSide: BorderSide(
color: Theme.of(context).focusColor, color: Theme.of(context).focusColor,
@ -183,8 +181,7 @@ class _RestoreFromKeysFromState extends State<RestoreFromKeysFrom> {
hintText: S.of(context).restore_spend_key_private, hintText: S.of(context).restore_spend_key_private,
focusedBorder: UnderlineInputBorder( focusedBorder: UnderlineInputBorder(
borderSide: BorderSide( borderSide: BorderSide(
color: Palette.cakeGreen, color: Palette.cakeGreen, width: 2.0)),
width: 2.0)),
enabledBorder: UnderlineInputBorder( enabledBorder: UnderlineInputBorder(
borderSide: BorderSide( borderSide: BorderSide(
color: Theme.of(context).focusColor, color: Theme.of(context).focusColor,

View file

@ -13,6 +13,7 @@ import 'package:cake_wallet/src/widgets/primary_button.dart';
import 'package:cake_wallet/palette.dart'; import 'package:cake_wallet/palette.dart';
class RestoreWalletFromSeedDetailsPage extends BasePage { class RestoreWalletFromSeedDetailsPage extends BasePage {
@override
String get title => S.current.restore_wallet_restore_description; String get title => S.current.restore_wallet_restore_description;
@override @override
@ -21,7 +22,8 @@ class RestoreWalletFromSeedDetailsPage extends BasePage {
class RestoreFromSeedDetailsForm extends StatefulWidget { class RestoreFromSeedDetailsForm extends StatefulWidget {
@override @override
createState() => _RestoreFromSeedDetailsFormState(); _RestoreFromSeedDetailsFormState createState() =>
_RestoreFromSeedDetailsFormState();
} }
class _RestoreFromSeedDetailsFormState class _RestoreFromSeedDetailsFormState
@ -34,14 +36,14 @@ class _RestoreFromSeedDetailsFormState
Widget build(BuildContext context) { Widget build(BuildContext context) {
final walletRestorationStore = Provider.of<WalletRestorationStore>(context); final walletRestorationStore = Provider.of<WalletRestorationStore>(context);
reaction((_) => walletRestorationStore.state, (state) { reaction((_) => walletRestorationStore.state, (WalletRestorationState state) {
if (state is WalletRestoredSuccessfully) { if (state is WalletRestoredSuccessfully) {
Navigator.of(context).popUntil((route) => route.isFirst); Navigator.of(context).popUntil((route) => route.isFirst);
} }
if (state is WalletRestorationFailure) { if (state is WalletRestorationFailure) {
WidgetsBinding.instance.addPostFrameCallback((_) { WidgetsBinding.instance.addPostFrameCallback((_) {
showDialog( showDialog<void>(
context: context, context: context,
builder: (BuildContext context) { builder: (BuildContext context) {
return AlertDialog( return AlertDialog(

View file

@ -14,24 +14,25 @@ import 'package:cake_wallet/src/stores/wallet_restoration/wallet_restoration_sto
import 'package:cake_wallet/src/widgets/seed_widget.dart'; import 'package:cake_wallet/src/widgets/seed_widget.dart';
class RestoreWalletFromSeedPage extends BasePage { class RestoreWalletFromSeedPage extends BasePage {
final WalletListService walletsService;
final WalletService walletService;
final SharedPreferences sharedPreferences;
String get title => S.current.restore_title_from_seed;
RestoreWalletFromSeedPage( RestoreWalletFromSeedPage(
{@required this.walletsService, {@required this.walletsService,
@required this.walletService, @required this.walletService,
@required this.sharedPreferences}); @required this.sharedPreferences});
final WalletListService walletsService;
final WalletService walletService;
final SharedPreferences sharedPreferences;
@override
String get title => S.current.restore_title_from_seed;
@override @override
Widget body(BuildContext context) => RestoreFromSeedForm(); Widget body(BuildContext context) => RestoreFromSeedForm();
} }
class RestoreFromSeedForm extends StatefulWidget { class RestoreFromSeedForm extends StatefulWidget {
@override @override
createState() => _RestoreFromSeedFormState(); _RestoreFromSeedFormState createState() => _RestoreFromSeedFormState();
} }
class _RestoreFromSeedFormState extends State<RestoreFromSeedForm> { class _RestoreFromSeedFormState extends State<RestoreFromSeedForm> {
@ -46,9 +47,8 @@ class _RestoreFromSeedFormState extends State<RestoreFromSeedForm> {
(_) => _setReactions(context, walletRestorationStore)); (_) => _setReactions(context, walletRestorationStore));
return GestureDetector( return GestureDetector(
onTap: () { onTap: () =>
SystemChannels.textInput.invokeMethod('TextInput.hide'); SystemChannels.textInput.invokeMethod<void>('TextInput.hide'),
},
child: Container( child: Container(
padding: EdgeInsets.only(left: 20.0, right: 20.0, bottom: 20.0), padding: EdgeInsets.only(left: 20.0, right: 20.0, bottom: 20.0),
child: Column( child: Column(
@ -90,7 +90,7 @@ class _RestoreFromSeedFormState extends State<RestoreFromSeedForm> {
return; return;
} }
reaction((_) => store.errorMessage, (errorMessage) { reaction((_) => store.errorMessage, (String errorMessage) {
if (errorMessage == null || errorMessage.isEmpty) { if (errorMessage == null || errorMessage.isEmpty) {
_seedKey.currentState.validated(); _seedKey.currentState.validated();
} else { } else {

View file

@ -9,7 +9,10 @@ import 'package:cake_wallet/generated/i18n.dart';
class RestoreWalletOptionsPage extends BasePage { class RestoreWalletOptionsPage extends BasePage {
static const _aspectRatioImage = 2.086; static const _aspectRatioImage = 2.086;
@override
String get title => S.current.restore_seed_keys_restore; String get title => S.current.restore_seed_keys_restore;
@override
Color get backgroundColor => Palette.creamyGrey; Color get backgroundColor => Palette.creamyGrey;
final _imageSeed = Image.asset('assets/images/seedIco.png'); final _imageSeed = Image.asset('assets/images/seedIco.png');
@ -29,8 +32,8 @@ class RestoreWalletOptionsPage extends BasePage {
Navigator.pushNamed(context, Routes.restoreWalletFromSeed), Navigator.pushNamed(context, Routes.restoreWalletFromSeed),
image: _imageSeed, image: _imageSeed,
aspectRatioImage: _aspectRatioImage, aspectRatioImage: _aspectRatioImage,
titleColor: Palette.lightViolet, titleColor: Palette.lightViolet,
color: Palette.lightViolet, color: Palette.lightViolet,
title: S.of(context).restore_title_from_seed, title: S.of(context).restore_title_from_seed,
description: S.of(context).restore_description_from_seed, description: S.of(context).restore_description_from_seed,
textButton: S.of(context).restore_next, textButton: S.of(context).restore_next,
@ -41,8 +44,8 @@ class RestoreWalletOptionsPage extends BasePage {
Navigator.pushNamed(context, Routes.restoreWalletFromKeys), Navigator.pushNamed(context, Routes.restoreWalletFromKeys),
image: _imageKeys, image: _imageKeys,
aspectRatioImage: _aspectRatioImage, aspectRatioImage: _aspectRatioImage,
titleColor: Palette.cakeGreen, titleColor: Palette.cakeGreen,
color: Palette.cakeGreen, color: Palette.cakeGreen,
title: S.of(context).restore_title_from_keys, title: S.of(context).restore_title_from_keys,
description: S.of(context).restore_description_from_keys, description: S.of(context).restore_description_from_keys,
textButton: S.of(context).restore_next, textButton: S.of(context).restore_next,

View file

@ -2,15 +2,6 @@ import 'package:flutter/material.dart';
import 'package:cake_wallet/palette.dart'; import 'package:cake_wallet/palette.dart';
class RestoreButton extends StatelessWidget { class RestoreButton extends StatelessWidget {
final VoidCallback onPressed;
final Image image;
final double aspectRatioImage;
final Color color;
final Color titleColor;
final String title;
final String description;
final String textButton;
const RestoreButton( const RestoreButton(
{@required this.onPressed, {@required this.onPressed,
@required this.image, @required this.image,
@ -21,9 +12,17 @@ class RestoreButton extends StatelessWidget {
this.description = '', this.description = '',
this.textButton = ''}); this.textButton = ''});
final VoidCallback onPressed;
final Image image;
final double aspectRatioImage;
final Color color;
final Color titleColor;
final String title;
final String description;
final String textButton;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Container( return Container(
margin: EdgeInsets.only(top: 20.0, bottom: 20.0), margin: EdgeInsets.only(top: 20.0, bottom: 20.0),
decoration: BoxDecoration( decoration: BoxDecoration(
@ -71,11 +70,11 @@ class RestoreButton extends StatelessWidget {
child: Text( child: Text(
description, description,
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: style: TextStyle(
TextStyle( color:
color: Theme.of(context).accentTextTheme.subhead.color, Theme.of(context).accentTextTheme.subhead.color,
fontSize: 14.0, fontSize: 14.0,
height: 1.4), height: 1.4),
), ),
) )
], ],
@ -88,7 +87,10 @@ class RestoreButton extends StatelessWidget {
decoration: BoxDecoration( decoration: BoxDecoration(
border: Border( border: Border(
top: BorderSide( top: BorderSide(
color: Theme.of(context).accentTextTheme.headline.decorationColor, color: Theme.of(context)
.accentTextTheme
.headline
.decorationColor,
width: 1.15)), width: 1.15)),
color: Colors.transparent, color: Colors.transparent,
), ),

View file

@ -1,11 +1,9 @@
import 'package:cake_wallet/src/screens/welcome/create_welcome_page.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:hive/hive.dart'; import 'package:hive/hive.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import 'package:cake_wallet/routes.dart'; import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/router.dart';
import 'package:cake_wallet/src/stores/authentication/authentication_store.dart'; import 'package:cake_wallet/src/stores/authentication/authentication_store.dart';
import 'package:cake_wallet/src/stores/price/price_store.dart'; import 'package:cake_wallet/src/stores/price/price_store.dart';
import 'package:cake_wallet/src/stores/settings/settings_store.dart'; import 'package:cake_wallet/src/stores/settings/settings_store.dart';
@ -19,6 +17,8 @@ import 'package:cake_wallet/src/domain/monero/transaction_description.dart';
import 'package:cake_wallet/src/screens/auth/create_login_page.dart'; import 'package:cake_wallet/src/screens/auth/create_login_page.dart';
import 'package:cake_wallet/src/screens/seed/create_seed_page.dart'; import 'package:cake_wallet/src/screens/seed/create_seed_page.dart';
import 'package:cake_wallet/src/screens/dashboard/create_dashboard_page.dart'; import 'package:cake_wallet/src/screens/dashboard/create_dashboard_page.dart';
import 'package:cake_wallet/src/screens/auth/auth_page.dart';
import 'package:cake_wallet/src/screens/welcome/create_welcome_page.dart';
class Root extends StatefulWidget { class Root extends StatefulWidget {
Root({Key key}) : super(key: key); Root({Key key}) : super(key: key);
@ -81,7 +81,7 @@ class RootState extends State<Root> with WidgetsBindingObserver {
WidgetsBinding.instance.addPostFrameCallback((_) { WidgetsBinding.instance.addPostFrameCallback((_) {
Navigator.of(context).pushNamed(Routes.unlock, Navigator.of(context).pushNamed(Routes.unlock,
arguments: (isAuthenticatedSuccessfully, auth) { arguments: (bool isAuthenticatedSuccessfully, AuthPageState auth) {
if (!isAuthenticatedSuccessfully) { if (!isAuthenticatedSuccessfully) {
return; return;
} }

View file

@ -11,14 +11,19 @@ import 'package:cake_wallet/src/stores/wallet_seed/wallet_seed_store.dart';
import 'package:cake_wallet/src/screens/base_page.dart'; import 'package:cake_wallet/src/screens/base_page.dart';
class SeedPage extends BasePage { class SeedPage extends BasePage {
SeedPage({this.onCloseCallback});
static final image = Image.asset('assets/images/seed_image.png'); static final image = Image.asset('assets/images/seed_image.png');
@override
bool get isModalBackButton => true; bool get isModalBackButton => true;
@override
String get title => S.current.seed_title; String get title => S.current.seed_title;
final VoidCallback onCloseCallback; final VoidCallback onCloseCallback;
SeedPage({this.onCloseCallback}); @override
void onClose(BuildContext context) => void onClose(BuildContext context) =>
onCloseCallback != null ? onCloseCallback() : Navigator.of(context).pop(); onCloseCallback != null ? onCloseCallback() : Navigator.of(context).pop();

View file

@ -1,3 +1,4 @@
import 'package:cake_wallet/src/screens/auth/auth_page.dart';
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart'; import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -23,8 +24,13 @@ import 'package:cake_wallet/src/domain/common/sync_status.dart';
import 'package:cake_wallet/src/stores/sync/sync_store.dart'; import 'package:cake_wallet/src/stores/sync/sync_store.dart';
class SendPage extends BasePage { class SendPage extends BasePage {
@override
String get title => S.current.send_title; String get title => S.current.send_title;
@override
bool get isModalBackButton => true; bool get isModalBackButton => true;
@override
bool get resizeToAvoidBottomPadding => false; bool get resizeToAvoidBottomPadding => false;
@override @override
@ -212,7 +218,7 @@ class SendFormState extends State<SendForm> {
signed: false, decimal: false), signed: false, decimal: false),
inputFormatters: [ inputFormatters: [
BlacklistingTextInputFormatter( BlacklistingTextInputFormatter(
new RegExp('[\\-|\\ |\\,]')) RegExp('[\\-|\\ |\\,]'))
], ],
decoration: InputDecoration( decoration: InputDecoration(
prefixIcon: Padding( prefixIcon: Padding(
@ -272,7 +278,7 @@ class SendFormState extends State<SendForm> {
signed: false, decimal: false), signed: false, decimal: false),
inputFormatters: [ inputFormatters: [
BlacklistingTextInputFormatter( BlacklistingTextInputFormatter(
new RegExp('[\\-|\\ |\\,]')) RegExp('[\\-|\\ |\\,]'))
], ],
decoration: InputDecoration( decoration: InputDecoration(
prefixIcon: Padding( prefixIcon: Padding(
@ -353,7 +359,7 @@ class SendFormState extends State<SendForm> {
FocusScope.of(context).requestFocus(FocusNode()); FocusScope.of(context).requestFocus(FocusNode());
if (_formKey.currentState.validate()) { if (_formKey.currentState.validate()) {
await showDialog( await showDialog<void>(
context: context, context: context,
builder: (dialogContext) { builder: (dialogContext) {
return AlertDialog( return AlertDialog(
@ -364,12 +370,11 @@ class SendFormState extends State<SendForm> {
FlatButton( FlatButton(
child: Text(S.of(context).send), child: Text(S.of(context).send),
onPressed: () async { onPressed: () async {
Navigator.of(dialogContext) await Navigator.of(dialogContext)
.popAndPushNamed( .popAndPushNamed(Routes.auth,
Routes.auth, arguments: (bool
arguments: isAuthenticatedSuccessfully,
(isAuthenticatedSuccessfully, AuthPageState auth) {
auth) {
if (!isAuthenticatedSuccessfully) { if (!isAuthenticatedSuccessfully) {
return; return;
} }
@ -408,13 +413,13 @@ class SendFormState extends State<SendForm> {
final sendStore = Provider.of<SendStore>(context); final sendStore = Provider.of<SendStore>(context);
reaction((_) => sendStore.fiatAmount, (amount) { reaction((_) => sendStore.fiatAmount, (String amount) {
if (amount != _fiatAmountController.text) { if (amount != _fiatAmountController.text) {
_fiatAmountController.text = amount; _fiatAmountController.text = amount;
} }
}); });
reaction((_) => sendStore.cryptoAmount, (amount) { reaction((_) => sendStore.cryptoAmount, (String amount) {
if (amount != _cryptoAmountController.text) { if (amount != _cryptoAmountController.text) {
_cryptoAmountController.text = amount; _cryptoAmountController.text = amount;
} }
@ -436,10 +441,10 @@ class SendFormState extends State<SendForm> {
} }
}); });
reaction((_) => sendStore.state, (state) { reaction((_) => sendStore.state, (SendingState state) {
if (state is SendingFailed) { if (state is SendingFailed) {
WidgetsBinding.instance.addPostFrameCallback((_) { WidgetsBinding.instance.addPostFrameCallback((_) {
showDialog( showDialog<void>(
context: context, context: context,
builder: (BuildContext context) { builder: (BuildContext context) {
return AlertDialog( return AlertDialog(
@ -457,7 +462,7 @@ class SendFormState extends State<SendForm> {
if (state is TransactionCreatedSuccessfully) { if (state is TransactionCreatedSuccessfully) {
WidgetsBinding.instance.addPostFrameCallback((_) { WidgetsBinding.instance.addPostFrameCallback((_) {
showDialog( showDialog<void>(
context: context, context: context,
builder: (BuildContext context) { builder: (BuildContext context) {
return AlertDialog( return AlertDialog(

View file

@ -21,7 +21,8 @@ const Map<String, String> _languages = {
}; };
class ChangeLanguage extends BasePage { class ChangeLanguage extends BasePage {
get title => S.current.settings_change_language; @override
String get title => S.current.settings_change_language;
@override @override
Widget body(BuildContext context) { Widget body(BuildContext context) {
@ -50,7 +51,7 @@ class ChangeLanguage extends BasePage {
), ),
onTap: () async { onTap: () async {
if (!isCurrent) { if (!isCurrent) {
await showDialog( await showDialog<void>(
context: context, context: context,
builder: (BuildContext context) { builder: (BuildContext context) {
return AlertDialog( return AlertDialog(

View file

@ -7,39 +7,41 @@ import 'package:cake_wallet/theme_changer.dart';
import 'package:cake_wallet/themes.dart'; import 'package:cake_wallet/themes.dart';
import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/generated/i18n.dart';
class EnterPinCode extends StatefulWidget{ class EnterPinCode extends StatefulWidget {
const EnterPinCode(this.currentPinLength, this.currentPin);
final int currentPinLength; final int currentPinLength;
final List<int> currentPin; final List<int> currentPin;
const EnterPinCode(this.currentPinLength, this.currentPin);
@override @override
createState() => EnterPinCodeState(currentPinLength, currentPin); EnterPinCodeState createState() =>
EnterPinCodeState(currentPinLength, currentPin);
} }
class EnterPinCodeState extends State<EnterPinCode>{ class EnterPinCodeState extends State<EnterPinCode> {
GlobalKey _gridViewKey = GlobalKey(); EnterPinCodeState(this.pinLength, this.currentPin);
final _gridViewKey = GlobalKey();
final _closeButtonImage = Image.asset('assets/images/close_button.png'); final _closeButtonImage = Image.asset('assets/images/close_button.png');
final _closeButtonImageDarkTheme = Image.asset('assets/images/close_button_dark_theme.png'); final _closeButtonImageDarkTheme =
Image.asset('assets/images/close_button_dark_theme.png');
static final deleteIconImage = Image.asset('assets/images/delete_icon.png'); static final deleteIconImage = Image.asset('assets/images/delete_icon.png');
final int pinLength; final int pinLength;
final List<int> currentPin; final List<int> currentPin;
List<int> pin; List<int> pin;
double _aspectRatio = 0; double _aspectRatio = 0;
EnterPinCodeState(this.pinLength, this.currentPin); void _calcualteCurrentAspectRatio() {
final renderBox =
_gridViewKey.currentContext.findRenderObject() as RenderBox;
final cellWidth = renderBox.size.width / 3;
final cellHeight = renderBox.size.height / 4;
_getCurrentAspectRatio(){ if (cellWidth > 0 && cellHeight > 0) {
final RenderBox renderBox = _gridViewKey.currentContext.findRenderObject(); _aspectRatio = cellWidth / cellHeight;
}
double cellWidth = renderBox.size.width/3; setState(() {});
double cellHeight = renderBox.size.height/4;
if (cellWidth > 0 && cellHeight > 0) _aspectRatio = cellWidth/cellHeight;
setState(() {
});
} }
@override @override
@ -49,130 +51,125 @@ class EnterPinCodeState extends State<EnterPinCode>{
WidgetsBinding.instance.addPostFrameCallback(_afterLayout); WidgetsBinding.instance.addPostFrameCallback(_afterLayout);
} }
_afterLayout(_) { void _afterLayout(dynamic _) => _calcualteCurrentAspectRatio();
_getCurrentAspectRatio();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final _themeChanger = Provider.of<ThemeChanger>(context);
ThemeChanger _themeChanger = Provider.of<ThemeChanger>(context); final _isDarkTheme = _themeChanger.getTheme() == Themes.darkTheme;
bool _isDarkTheme;
if (_themeChanger.getTheme() == Themes.darkTheme) _isDarkTheme = true;
else _isDarkTheme = false;
return Scaffold( return Scaffold(
backgroundColor: Theme.of(context).backgroundColor, backgroundColor: Theme.of(context).backgroundColor,
appBar: CupertinoNavigationBar( appBar: CupertinoNavigationBar(
leading: ButtonTheme( leading: ButtonTheme(
minWidth: double.minPositive, minWidth: double.minPositive,
child: FlatButton( child: FlatButton(
onPressed: (){ Navigator.pop(context, false); }, onPressed: () {
child: _isDarkTheme ? _closeButtonImageDarkTheme : _closeButtonImage Navigator.pop(context, false);
},
child: _isDarkTheme
? _closeButtonImageDarkTheme
: _closeButtonImage),
), ),
), backgroundColor: Theme.of(context).backgroundColor,
backgroundColor: Theme.of(context).backgroundColor, border: null),
border: null,
),
body: SafeArea( body: SafeArea(
child: Container( child: Container(
padding: EdgeInsets.only(left: 40.0, right: 40.0, bottom: 40.0), padding: EdgeInsets.only(left: 40.0, right: 40.0, bottom: 40.0),
child: Column( child: Column(
children: <Widget>[ children: <Widget>[
Spacer(flex: 2), Spacer(flex: 2),
Text(S.of(context).enter_your_pin, Text(S.of(context).enter_your_pin,
style: TextStyle( style: TextStyle(fontSize: 24, color: Palette.wildDarkBlue)),
fontSize: 24, Spacer(flex: 3),
color: Palette.wildDarkBlue Container(
) width: 180,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: List.generate(pinLength, (index) {
const size = 10.0;
final isFilled = pin[index] != null;
return Container(
width: size,
height: size,
decoration: BoxDecoration(
shape: BoxShape.circle,
color:
isFilled ? Palette.deepPurple : Colors.transparent,
border: Border.all(color: Palette.wildDarkBlue),
));
}),
), ),
Spacer(flex: 3), ),
Container( Spacer(flex: 3),
width: 180, Flexible(
child: Row( flex: 24,
mainAxisAlignment: MainAxisAlignment.spaceBetween, child: Container(
children: List.generate(pinLength, (index) { key: _gridViewKey,
const size = 10.0; child: _aspectRatio > 0
final isFilled = pin[index] != null; ? GridView.count(
crossAxisCount: 3,
childAspectRatio: _aspectRatio,
physics: const NeverScrollableScrollPhysics(),
children: List.generate(12, (index) {
if (index == 9) {
return Container(
margin: EdgeInsets.all(5.0),
decoration: BoxDecoration(
shape: BoxShape.circle,
color: _isDarkTheme
? PaletteDark.darkThemePinButton
: Palette.darkGrey,
),
);
} else if (index == 10) {
index = 0;
} else if (index == 11) {
return Container(
margin: EdgeInsets.all(5.0),
child: FlatButton(
onPressed: () {
_pop();
},
color: _isDarkTheme
? PaletteDark.darkThemePinButton
: Palette.darkGrey,
shape: CircleBorder(),
child: deleteIconImage,
),
);
} else {
index++;
}
return Container( return Container(
width: size, margin: EdgeInsets.all(5.0),
height: size, child: FlatButton(
decoration: BoxDecoration( onPressed: () {
shape: BoxShape.circle, _push(index);
color: isFilled ? Palette.deepPurple : Colors.transparent, },
border: Border.all(color: Palette.wildDarkBlue), color: _isDarkTheme
)); ? PaletteDark.darkThemePinDigitButton
}), : Palette.creamyGrey,
), shape: CircleBorder(),
), child: Text('$index',
Spacer(flex: 3), style: TextStyle(
Flexible( fontSize: 23.0,
flex: 24, color: Palette.blueGrey)),
child: Container( ),
key: _gridViewKey, );
child: _aspectRatio > 0 ? GridView.count( }),
crossAxisCount: 3, )
childAspectRatio: _aspectRatio, : null))
physics: const NeverScrollableScrollPhysics(), ],
children: List.generate(12, (index) { ),
)),
if (index == 9) {
return Container(
margin: EdgeInsets.all(5.0),
decoration: BoxDecoration(
shape: BoxShape.circle,
color: _isDarkTheme ? PaletteDark.darkThemePinButton
: Palette.darkGrey,
),
);
} else if (index == 10) {
index = 0;
} else if (index == 11) {
return Container(
margin: EdgeInsets.all(5.0),
child: FlatButton(
onPressed: () { _pop(); },
color: _isDarkTheme ? PaletteDark.darkThemePinButton
: Palette.darkGrey,
shape: CircleBorder(),
child: deleteIconImage,
),
);
} else {
index++;
}
return Container(
margin: EdgeInsets.all(5.0),
child: FlatButton(
onPressed: () { _push(index); },
color: _isDarkTheme ? PaletteDark.darkThemePinDigitButton
: Palette.creamyGrey,
shape: CircleBorder(),
child: Text(
'$index',
style: TextStyle(
fontSize: 23.0,
color: Palette.blueGrey
)
),
),
);
}),
) : null
)
)
],
),
)
),
); );
} }
_showIncorrectPinDialog(BuildContext context) async { void _showIncorrectPinDialog(BuildContext context) async {
await showDialog( await showDialog<void>(
context: context, context: context,
builder: (BuildContext context) { builder: (BuildContext context) {
return AlertDialog( return AlertDialog(
@ -186,8 +183,7 @@ class EnterPinCodeState extends State<EnterPinCode>{
), ),
], ],
); );
} });
);
} }
void _push(int num) { void _push(int num) {
@ -202,20 +198,16 @@ class EnterPinCodeState extends State<EnterPinCode>{
} }
} }
if (_pinLength() == pinLength) { final currentPinLength = _pinLength();
if (listEquals<int>(pin, currentPin)){
if (currentPinLength == pinLength) {
if (listEquals<int>(pin, currentPin)) {
Navigator.pop(context, true); Navigator.pop(context, true);
} else { } else {
Navigator.pop(context, false); Navigator.pop(context, false);
_showIncorrectPinDialog(context); _showIncorrectPinDialog(context);
} }
} }
} }
@ -226,7 +218,7 @@ class EnterPinCodeState extends State<EnterPinCode>{
for (var i = pin.length - 1; i >= 0; i--) { for (var i = pin.length - 1; i >= 0; i--) {
if (pin[i] != null) { if (pin[i] != null) {
setState(() => pin[i] = null); setState(() => pin[i] = null);
break; break;
} }
} }
@ -241,5 +233,4 @@ class EnterPinCodeState extends State<EnterPinCode>{
return v; return v;
}); });
} }
} }

View file

@ -2,6 +2,15 @@ import 'package:flutter/material.dart';
import 'package:cake_wallet/src/screens/settings/attributes.dart'; import 'package:cake_wallet/src/screens/settings/attributes.dart';
class SettingsItem { class SettingsItem {
SettingsItem(
{this.onTaped,
this.title,
this.link,
this.image,
this.widget,
this.attribute,
this.widgetBuilder});
final VoidCallback onTaped; final VoidCallback onTaped;
final String title; final String title;
final String link; final String link;
@ -9,12 +18,4 @@ class SettingsItem {
final Widget widget; final Widget widget;
final Attributes attribute; final Attributes attribute;
final WidgetBuilder widgetBuilder; final WidgetBuilder widgetBuilder;
SettingsItem({this.onTaped,
this.title,
this.link,
this.image,
this.widget,
this.attribute,
this.widgetBuilder});
} }

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