Merged 4.0.9

This commit is contained in:
M
2020-12-11 20:10:06 +02:00
52 changed files with 986 additions and 126 deletions
+1 -1
View File
@@ -60,7 +60,7 @@ class AddressValidator extends TextValidator {
case CryptoCurrency.bnb:
return [42];
case CryptoCurrency.btc:
return [33, 34, 42];
return [33, 34, 42, 62];
case CryptoCurrency.dai:
return [42];
case CryptoCurrency.dash:
+5 -1
View File
@@ -4,7 +4,11 @@ class InitialExecutionState extends ExecutionState {}
class IsExecutingState extends ExecutionState {}
class ExecutedSuccessfullyState extends ExecutionState {}
class ExecutedSuccessfullyState extends ExecutionState {
ExecutedSuccessfullyState({this.payload});
final dynamic payload;
}
class FailureState extends ExecutionState {
FailureState(this.error);
+1 -1
View File
@@ -7,7 +7,7 @@ class MoneroLabelValidator extends TextValidator {
MoneroLabelValidator({@required CryptoCurrency type})
: super(
errorMessage: S.current.error_text_account_name,
pattern: '^[a-zA-Z0-9_]{1,15}\$',
pattern: '^[a-zA-Z0-9_ ]{1,15}\$',
minLength: 1,
maxLength: 15);
}
+18 -2
View File
@@ -22,6 +22,7 @@ import 'package:cake_wallet/src/screens/nodes/nodes_list_page.dart';
import 'package:cake_wallet/src/screens/pin_code/pin_code_widget.dart';
import 'package:cake_wallet/src/screens/rescan/rescan_page.dart';
import 'package:cake_wallet/src/screens/restore/wallet_restore_page.dart';
import 'package:cake_wallet/src/screens/seed/pre_seed_page.dart';
import 'package:cake_wallet/src/screens/seed/wallet_seed_page.dart';
import 'package:cake_wallet/src/screens/send/send_template_page.dart';
import 'package:cake_wallet/src/screens/settings/change_language.dart';
@@ -49,6 +50,7 @@ import 'package:cake_wallet/store/wallet_list_store.dart';
import 'package:cake_wallet/view_model/contact_list/contact_list_view_model.dart';
import 'package:cake_wallet/view_model/contact_list/contact_view_model.dart';
import 'package:cake_wallet/view_model/exchange/exchange_trade_view_model.dart';
import 'package:cake_wallet/view_model/monero_account_list/account_list_item.dart';
import 'package:cake_wallet/view_model/node_list/node_list_view_model.dart';
import 'package:cake_wallet/view_model/node_list/node_create_or_edit_view_model.dart';
import 'package:cake_wallet/view_model/rescan_view_model.dart';
@@ -274,7 +276,7 @@ Future setup(
getIt.registerFactory(() => MoneroAccountListPage(
accountListViewModel: getIt.get<MoneroAccountListViewModel>()));
getIt.registerFactory(() {
/*getIt.registerFactory(() {
final wallet = getIt.get<AppStore>().wallet;
if (wallet is MoneroWallet) {
@@ -287,7 +289,19 @@ Future setup(
getIt.registerFactory(() => MoneroAccountEditOrCreatePage(
moneroAccountCreationViewModel:
getIt.get<MoneroAccountEditOrCreateViewModel>()));
getIt.get<MoneroAccountEditOrCreateViewModel>()));*/
getIt.registerFactoryParam<MoneroAccountEditOrCreateViewModel,
AccountListItem, void>(
(AccountListItem account, _) => MoneroAccountEditOrCreateViewModel(
(getIt.get<AppStore>().wallet as MoneroWallet).accountList,
accountListItem: account));
getIt.registerFactoryParam<MoneroAccountEditOrCreatePage, AccountListItem,
void>(
(AccountListItem account, _) => MoneroAccountEditOrCreatePage(
moneroAccountCreationViewModel:
getIt.get<MoneroAccountEditOrCreateViewModel>(param1: account)));
getIt.registerFactory(() {
final appStore = getIt.get<AppStore>();
@@ -411,4 +425,6 @@ Future setup(
void Function(BuildContext, WalletType), bool>(
(para1, param2) => NewWalletTypePage(getIt.get<WalletNewVM>(),
onTypeSelected: para1, isNewWallet: param2));
getIt.registerFactory(() => PreSeedPage());
}
+9 -2
View File
@@ -14,7 +14,8 @@ class Node extends HiveObject with Keyable {
{@required this.uri,
@required WalletType type,
this.login,
this.password}) {
this.password,
this.useSSL}) {
this.type = type;
}
@@ -22,7 +23,8 @@ class Node extends HiveObject with Keyable {
: uri = map['uri'] as String ?? '',
login = map['login'] as String,
password = map['password'] as String,
typeRaw = map['typeRaw'] as int;
typeRaw = map['typeRaw'] as int,
useSSL = map['useSSL'] as bool;
static const boxName = 'Nodes';
@@ -38,6 +40,11 @@ class Node extends HiveObject with Keyable {
@HiveField(3)
int typeRaw;
@HiveField(4)
bool useSSL;
bool get isSSL => useSSL ?? false;
@override
dynamic get keyIndex {
_keyIndex ??= key;
+2 -1
View File
@@ -14,7 +14,8 @@ class TradeIsCreatedSuccessfully extends ExchangeTradeState {
}
class TradeIsCreatedFailure extends ExchangeTradeState {
TradeIsCreatedFailure({@required this.error});
TradeIsCreatedFailure({@required this.title, @required this.error});
final String title;
final String error;
}
@@ -12,6 +12,7 @@ import 'package:cake_wallet/exchange/xmrto/xmrto_trade_request.dart';
import 'package:cake_wallet/exchange/trade_not_created_exeption.dart';
import 'package:cake_wallet/exchange/exchange_provider_description.dart';
import 'package:cake_wallet/exchange/trade_not_found_exeption.dart';
import 'package:cake_wallet/generated/i18n.dart';
class XMRTOExchangeProvider extends ExchangeProvider {
XMRTOExchangeProvider()
@@ -90,12 +91,25 @@ class XMRTOExchangeProvider extends ExchangeProvider {
Future<Trade> createTrade({TradeRequest request}) async {
final _request = request as XMRTOTradeRequest;
final url = originalApiUri + _orderCreateUriSuffix;
final _amount = _request.isBTCRequest
? _request.receiveAmount
: _request.amount;
final _amountCurrency = _request.isBTCRequest
? _request.to.toString()
: _request.from.toString();
final pattern = '^([0-9]+([.\,][0-9]{0,8})?|[.\,][0-9]{1,8})\$';
final isValid = RegExp(pattern).hasMatch(_amount);
if (!isValid) {
throw TradeNotCreatedException(description,
description: S.current.xmr_to_error_description);
}
final body = {
'amount':
_request.isBTCRequest ? _request.receiveAmount : _request.amount,
'amount_currency': _request.isBTCRequest
? _request.to.toString()
: _request.from.toString(),
'amount': _amount,
'amount_currency': _amountCurrency,
'btc_dest_address': _request.address
};
final response = await post(url,
File diff suppressed because it is too large Load Diff
+12 -5
View File
@@ -40,6 +40,10 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance> with Store {
fullBalance: monero_wallet.getFullBalance(accountIndex: 0),
unlockedBalance: monero_wallet.getFullBalance(accountIndex: 0));
_onAccountChangeReaction = reaction((_) => account, (Account account) {
balance = MoneroBalance(
fullBalance: monero_wallet.getFullBalance(accountIndex: account.id),
unlockedBalance:
monero_wallet.getUnlockedBalance(accountIndex: account.id));
subaddressList.update(accountIndex: account.id);
subaddress = subaddressList.subaddresses.first;
address = subaddress.address;
@@ -94,7 +98,7 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance> with Store {
bool _isSavingAfterNewTransaction;
Future<void> init() async {
await accountList.update();
accountList.update();
account = accountList.accounts.first;
subaddressList.update(accountIndex: account.id ?? 0);
subaddress = subaddressList.getAll().first;
@@ -147,8 +151,7 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance> with Store {
address: node.uri,
login: node.login,
password: node.password,
useSSL: false,
// FIXME: hardcoded value
useSSL: node.isSSL,
isLightWallet: false); // FIXME: hardcoded value
syncStatus = ConnectedSyncStatus();
} catch (e) {
@@ -256,6 +259,7 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance> with Store {
monero_wallet.rescanBlockchainAsync();
await startSync();
_askForUpdateBalance();
accountList.update();
await _askForUpdateTransactionHistory();
await save();
await walletInfo.save();
@@ -363,17 +367,20 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance> with Store {
void _onNewBlock(int height, int blocksLeft, double ptc) async {
if (walletInfo.isRecovery) {
_askForUpdateTransactionHistory();
await _askForUpdateTransactionHistory();
_askForUpdateBalance();
accountList.update();
}
if (blocksLeft < 100) {
await _askForUpdateTransactionHistory();
_askForUpdateBalance();
accountList.update();
syncStatus = SyncedSyncStatus();
await _afterSyncSave();
if (walletInfo.isRecovery) {
setAsRecovered();
await setAsRecovered();
}
} else {
syncStatus = SyncingSyncStatus(blocksLeft, ptc);
+9 -2
View File
@@ -2,7 +2,9 @@ import 'package:cake_wallet/entities/contact_record.dart';
import 'package:cake_wallet/entities/transaction_description.dart';
import 'package:cake_wallet/src/screens/pin_code/pin_code_widget.dart';
import 'package:cake_wallet/src/screens/restore/wallet_restore_page.dart';
import 'package:cake_wallet/src/screens/seed/pre_seed_page.dart';
import 'package:cake_wallet/store/settings_store.dart';
import 'package:cake_wallet/view_model/monero_account_list/account_list_item.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/routes.dart';
@@ -70,7 +72,7 @@ Route<dynamic> createRoute(RouteSettings settings) {
return CupertinoPageRoute<void>(
builder: (_) => getIt.get<NewWalletTypePage>(
param1: (BuildContext context, WalletType _) =>
Navigator.of(context).pushNamed(Routes.seed, arguments: true),
Navigator.of(context).pushNamed(Routes.preSeed, arguments: true),
param2: true));
case Routes.newWallet:
@@ -245,7 +247,8 @@ Route<dynamic> createRoute(RouteSettings settings) {
case Routes.accountCreation:
return CupertinoPageRoute<String>(
builder: (_) => getIt.get<MoneroAccountEditOrCreatePage>());
builder: (_) => getIt.get<MoneroAccountEditOrCreatePage>(
param1: settings.arguments as AccountListItem));
case Routes.addressBook:
return MaterialPageRoute<void>(
@@ -306,6 +309,10 @@ Route<dynamic> createRoute(RouteSettings settings) {
return MaterialPageRoute<void>(
builder: (_) => getIt.get<LanguageListPage>());
case Routes.preSeed:
return MaterialPageRoute<void>(
builder: (_) => getIt.get<PreSeedPage>());
default:
return MaterialPageRoute<void>(
builder: (_) => Scaffold(
+1
View File
@@ -47,4 +47,5 @@ class Routes {
static const exchangeTemplate = '/exchange_template';
static const restoreWalletType = '/restore_wallet_type';
static const restoreWallet = '/restore_wallet';
static const preSeed = '/pre_seed';
}
@@ -39,7 +39,7 @@ class AddressPage extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(
S.of(context).addresses,
S.of(context).accounts_subaddresses,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
@@ -5,6 +5,7 @@ import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
import 'package:cake_wallet/entities/wallet_type.dart';
import 'package:cake_wallet/src/screens/dashboard/wallet_menu.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
// FIXME: terrible design.
@@ -147,7 +148,7 @@ class MenuWidgetState extends State<MenuWidget> {
),
if (widget.dashboardViewModel.subname !=
null)
Text(
Observer(builder: (_) => Text(
widget.dashboardViewModel.subname,
style: TextStyle(
color: Theme.of(context)
@@ -156,7 +157,7 @@ class MenuWidgetState extends State<MenuWidget> {
.decorationColor,
fontWeight: FontWeight.w500,
fontSize: 12),
)
))
],
),
))
+2 -1
View File
@@ -547,7 +547,7 @@ class ExchangePage extends BasePage {
context: context,
builder: (BuildContext context) {
return AlertWithOneAction(
alertTitle: S.of(context).error,
alertTitle: S.of(context).provider_error(state.title),
alertContent: state.error,
buttonText: S.of(context).ok,
buttonAction: () => Navigator.of(context).pop());
@@ -555,6 +555,7 @@ class ExchangePage extends BasePage {
});
}
if (state is TradeIsCreatedSuccessfully) {
exchangeViewModel.reset();
Navigator.of(context).pushNamed(Routes.exchangeConfirm);
}
});
@@ -103,7 +103,12 @@ class MoneroAccountListPage extends StatelessWidget {
accountListViewModel
.select(account);
Navigator.of(context).pop();
});
},
onEdit: () async =>
await Navigator.of(context)
.pushNamed(
Routes.accountCreation,
arguments: account));
},
),
isAlwaysShowScrollThumb
@@ -1,15 +1,19 @@
import 'package:flutter/material.dart';
import 'package:flutter_slidable/flutter_slidable.dart';
import 'package:cake_wallet/generated/i18n.dart';
class AccountTile extends StatelessWidget {
AccountTile({
@required this.isCurrent,
@required this.accountName,
@required this.onTap
@required this.onTap,
@required this.onEdit
});
final bool isCurrent;
final String accountName;
final VoidCallback onTap;
final Function() onTap;
final Function() onEdit;
@override
Widget build(BuildContext context) {
@@ -20,7 +24,7 @@ class AccountTile extends StatelessWidget {
? Theme.of(context).textTheme.subtitle.color
: Theme.of(context).textTheme.display4.color;
return GestureDetector(
final Widget cell = GestureDetector(
onTap: onTap,
child: Container(
height: 77,
@@ -39,5 +43,17 @@ class AccountTile extends StatelessWidget {
),
),
);
return isCurrent ? cell : Slidable(
key: Key(accountName),
child: cell,
actionPane: SlidableDrawerActionPane(),
secondaryActions: <Widget>[
IconSlideAction(
caption: S.of(context).edit,
color: Colors.blue,
icon: Icons.edit,
onTap: () => onEdit?.call())
]);
}
}
@@ -1,3 +1,7 @@
import 'package:cake_wallet/core/execution_state.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cake_wallet/src/widgets/standard_checkbox.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
@@ -67,6 +71,39 @@ class NodeCreateOrEditPage extends BasePage {
@override
Widget body(BuildContext context) {
reaction((_) => nodeCreateOrEditViewModel.connectionState,
(ExecutionState state) {
if (state is ExecutedSuccessfullyState) {
WidgetsBinding.instance.addPostFrameCallback((_) {
showPopUp<void>(
context: context,
builder: (BuildContext context) =>
AlertWithOneAction(
alertTitle: S.of(context).new_node_testing,
alertContent: state.payload as bool
? S.of(context).node_connection_successful
: S.of(context).node_connection_failed,
buttonText: S.of(context).ok,
buttonAction: () => Navigator.of(context).pop()));
});
}
if (state is FailureState) {
WidgetsBinding.instance.addPostFrameCallback((_) {
showPopUp<void>(
context: context,
builder: (BuildContext context) {
return AlertWithOneAction(
alertTitle: S.of(context).error,
alertContent: state.error,
buttonText: S.of(context).ok,
buttonAction: () => Navigator.of(context).pop());
});
});
}
});
return Container(
padding: EdgeInsets.only(left: 24, right: 24),
child: ScrollableWithBottomSection(
@@ -122,7 +159,22 @@ class NodeCreateOrEditPage extends BasePage {
)
)
],
)
),
Padding(
padding: EdgeInsets.only(top: 20),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
children: [
Observer(
builder: (_) => StandardCheckbox(
value: nodeCreateOrEditViewModel.useSSL,
onChanged: (value) =>
nodeCreateOrEditViewModel.useSSL = value,
caption: S.of(context).use_ssl,
))
],
))
]
],
)),
@@ -133,9 +185,18 @@ class NodeCreateOrEditPage extends BasePage {
Flexible(
child: Container(
padding: EdgeInsets.only(right: 8.0),
child: PrimaryButton(
onPressed: () => nodeCreateOrEditViewModel.reset(),
text: S.of(context).reset,
child: LoadingPrimaryButton(
onPressed: () async {
if (!_formKey.currentState.validate()) {
return;
}
await nodeCreateOrEditViewModel.connect();
},
isLoading: nodeCreateOrEditViewModel
.connectionState is IsExecutingState,
text: S.of(context).node_test,
isDisabled: !nodeCreateOrEditViewModel.isReady,
color: Colors.orange,
textColor: Colors.white),
)),
@@ -154,7 +215,9 @@ class NodeCreateOrEditPage extends BasePage {
text: S.of(context).save,
color: Theme.of(context).accentTextTheme.body2.color,
textColor: Colors.white,
isDisabled: !nodeCreateOrEditViewModel.isReady,
isDisabled: (!nodeCreateOrEditViewModel.isReady)||
(nodeCreateOrEditViewModel
.connectionState is IsExecutingState),
),
)),
],
+2 -2
View File
@@ -99,9 +99,9 @@ class NodeListPage extends BasePage {
await showPopUp<void>(
context: context,
builder: (BuildContext context) {
// FIXME: Add translation.
return AlertWithTwoActions(
alertTitle: 'Change current node',
alertTitle: S.of(context)
.change_current_node_title,
alertContent: S
.of(context)
.change_current_node(node.uri),
+14 -15
View File
@@ -117,7 +117,7 @@ class ReceivePage extends BasePage {
Icons.arrow_forward_ios,
size: 14,
color:
Theme.of(context).textTheme.display1.color,
Theme.of(context).textTheme.display1.color,
));
}
@@ -130,7 +130,7 @@ class ReceivePage extends BasePage {
Icons.add,
size: 20,
color:
Theme.of(context).textTheme.display1.color,
Theme.of(context).textTheme.display1.color,
));
}
@@ -140,13 +140,13 @@ class ReceivePage extends BasePage {
addressListViewModel.address.address;
final backgroundColor = isCurrent
? Theme.of(context)
.textTheme
.display3
.decorationColor
.textTheme
.display3
.decorationColor
: Theme.of(context)
.textTheme
.display2
.decorationColor;
.textTheme
.display2
.decorationColor;
final textColor = isCurrent
? Theme.of(context).textTheme.display3.color
: Theme.of(context).textTheme.display2.color;
@@ -155,8 +155,7 @@ class ReceivePage extends BasePage {
isCurrent: isCurrent,
backgroundColor: backgroundColor,
textColor: textColor,
onTap: (_) =>
addressListViewModel.setAddress(item),
onTap: (_) => addressListViewModel.setAddress(item),
onEdit: () => Navigator.of(context).pushNamed(
Routes.newSubaddress,
arguments: item));
@@ -166,11 +165,11 @@ class ReceivePage extends BasePage {
return index != 0
? cell
: ClipRRect(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(30),
topRight: Radius.circular(30)),
child: cell,
);
borderRadius: BorderRadius.only(
topLeft: Radius.circular(30),
topRight: Radius.circular(30)),
child: cell,
);
})),
],
),
@@ -14,6 +14,7 @@ class AddressCell extends StatelessWidget {
address: item.address,
name: item.name,
isCurrent: isCurrent,
isPrimary: item.isPrimary,
backgroundColor: backgroundColor,
textColor: textColor,
onTap: onTap,
@@ -23,6 +24,7 @@ class AddressCell extends StatelessWidget {
{@required this.address,
@required this.name,
@required this.isCurrent,
@required this.isPrimary,
@required this.backgroundColor,
@required this.textColor,
this.onTap,
@@ -31,6 +33,7 @@ class AddressCell extends StatelessWidget {
final String address;
final String name;
final bool isCurrent;
final bool isPrimary;
final Color backgroundColor;
final Color textColor;
final Function(String) onTap;
@@ -56,7 +59,7 @@ class AddressCell extends StatelessWidget {
),
));
return isCurrent
return (isCurrent || isPrimary)
? cell
: Slidable(
key: Key(address),
+1 -11
View File
@@ -52,19 +52,9 @@ class QRWidget extends StatelessWidget {
))))),
Spacer(flex: 3)
]),
Padding(
padding: EdgeInsets.only(top: 20),
child: Text(
S.of(context).scan_qr_code,
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Theme.of(context).indicatorColor),
),
),
isAmountFieldShow
? Padding(
padding: EdgeInsets.only(top: 40),
padding: EdgeInsets.only(top: 60),
child: Row(
children: <Widget>[
Expanded(
+67
View File
@@ -0,0 +1,67 @@
import 'package:cake_wallet/di.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/store/settings_store.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/widgets/primary_button.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
class PreSeedPage extends BasePage {
static final imageLight = Image.asset('assets/images/pre_seed_light.png');
static final imageDark = Image.asset('assets/images/pre_seed_dark.png');
@override
Widget leading(BuildContext context) => null;
@override
String get title => S.current.pre_seed_title;
@override
Widget body(BuildContext context) {
final image =
getIt.get<SettingsStore>().isDarkTheme ? imageDark : imageLight;
return WillPopScope(
onWillPop: () async => false,
child: Container(
padding: EdgeInsets.all(24),
child: Column(
children: [
Flexible(
flex: 2,
child: AspectRatio(
aspectRatio: 1,
child: FittedBox(child: image, fit: BoxFit.contain))),
Flexible(
flex: 3,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Padding(
padding: EdgeInsets.only(top: 70, left: 16, right: 16),
child: Text(
S.of(context).pre_seed_description,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.normal,
color: Theme.of(context)
.primaryTextTheme
.caption
.color),
),
),
PrimaryButton(
onPressed: () => Navigator.of(context)
.popAndPushNamed(Routes.seed, arguments: true),
text: S.of(context).pre_seed_button_text,
color: Theme.of(context).accentTextTheme.body2.color,
textColor: Colors.white)
],
))
],
),
));
}
}
+2 -2
View File
@@ -86,7 +86,7 @@ class WalletSeedPage extends BasePage {
final image =
getIt.get<SettingsStore>().isDarkTheme ? imageDark : imageLight;
return Container(
return WillPopScope(onWillPop: () async => false, child: Container(
padding: EdgeInsets.all(24),
child: Column(
children: <Widget>[
@@ -194,6 +194,6 @@ class WalletSeedPage extends BasePage {
],
))
],
));
)));
}
}
@@ -243,7 +243,7 @@ class WalletListBodyState extends State<WalletListBody> {
changeProcessText(S.of(context).creating_new_wallet);
await widget.walletListViewModel.walletNewVM.create(options: 'English'); // FIXME: Unnamed constant
hideProgressText();
await Navigator.of(context).pushNamed(Routes.seed, arguments: true);
await Navigator.of(context).pushNamed(Routes.preSeed);
} catch(e) {
changeProcessText(S.of(context).creating_new_wallet_error(e.toString()));
}
+76
View File
@@ -0,0 +1,76 @@
import 'dart:ui';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class StandardCheckbox extends StatefulWidget {
StandardCheckbox({
@required this.value,
this.caption = '',
@required this.onChanged});
final bool value;
final String caption;
final Function(bool) onChanged;
@override
StandardCheckboxState createState() =>
StandardCheckboxState(value, caption, onChanged);
}
class StandardCheckboxState extends State<StandardCheckbox> {
StandardCheckboxState(this.value, this.caption, this.onChanged);
bool value;
String caption;
Function(bool) onChanged;
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
value = !value;
onChanged(value);
setState(() {});
},
child: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Container(
height: 24.0,
width: 24.0,
margin: EdgeInsets.only(
right: 10.0,
),
decoration: BoxDecoration(
border: Border.all(
color: Theme.of(context)
.primaryTextTheme
.caption
.color,
width: 1.0),
borderRadius: BorderRadius.all(
Radius.circular(8.0)),
color: Theme.of(context).backgroundColor),
child: value
? Icon(
Icons.check,
color: Colors.blue,
size: 20.0,
)
: Offstage(),
),
Text(
caption,
style: TextStyle(
fontSize: 16.0,
color: Theme.of(context)
.primaryTextTheme
.title
.color),
)
],
),
);
}
}
+1 -1
View File
@@ -3,7 +3,7 @@ import 'package:mobx/mobx.dart';
import 'package:cake_wallet/exchange/exchange_provider_description.dart';
import 'package:cake_wallet/view_model/dashboard/trade_list_item.dart';
part 'trade_filter_store.g.dart';
part'trade_filter_store.g.dart';
class TradeFilterStore = TradeFilterStoreBase with _$TradeFilterStore;
@@ -1,5 +1,10 @@
import 'package:cake_wallet/bitcoin/bitcoin_transaction_info.dart';
import 'package:cake_wallet/bitcoin/bitcoin_wallet.dart';
import 'package:cake_wallet/entities/transaction_history.dart';
import 'package:cake_wallet/monero/account.dart';
import 'package:cake_wallet/monero/monero_balance.dart';
import 'package:cake_wallet/monero/monero_transaction_history.dart';
import 'package:cake_wallet/monero/monero_transaction_info.dart';
import 'package:cake_wallet/monero/monero_wallet.dart';
import 'package:cake_wallet/entities/balance_display_mode.dart';
import 'package:cake_wallet/entities/crypto_currency.dart';
@@ -74,28 +79,53 @@ abstract class DashboardViewModelBase with Store {
wallet ??= appStore.wallet;
type = wallet.type;
transactions = ObservableList.of(wallet
.transactionHistory.transactions.values
.map((transaction) => TransactionListItem(
transaction: transaction,
balanceViewModel: balanceViewModel,
settingsStore: appStore.settingsStore)));
_reaction = reaction((_) => appStore.wallet, _onWalletChange);
// FIXME: fixme
final _wallet = wallet;
if (_wallet is MoneroWallet) {
subname = _wallet.account?.label;
_onMoneroAccountChangeReaction = reaction((_) => _wallet.account,
(Account account) => _onMoneroAccountChange(_wallet));
_onMoneroBalanceChangeReaction = reaction((_) => _wallet.balance,
(MoneroBalance balance) => _onMoneroTransactionsUpdate(_wallet));
final _accountTransactions = _wallet
.transactionHistory.transactions.values
.where((tx) => tx.accountIndex == _wallet.account.id)
.toList();
transactions = ObservableList.of(_accountTransactions.map((transaction) =>
TransactionListItem(
transaction: transaction,
balanceViewModel: balanceViewModel,
settingsStore: appStore.settingsStore)));
} else {
transactions = ObservableList.of(wallet
.transactionHistory.transactions.values
.map((transaction) => TransactionListItem(
transaction: transaction,
balanceViewModel: balanceViewModel,
settingsStore: appStore.settingsStore)));
}
connectMapToListWithTransform(
appStore.wallet.transactionHistory.transactions,
transactions,
(TransactionInfo val) => TransactionListItem(
transaction: val,
balanceViewModel: balanceViewModel,
settingsStore: appStore.settingsStore));
settingsStore: appStore.settingsStore),
filter: (TransactionInfo tx) {
final wallet = _wallet;
if (tx is MoneroTransactionInfo && wallet is MoneroWallet) {
return tx.accountIndex == wallet.account.id;
}
final _wallet = wallet;
if (_wallet is MoneroWallet) {
subname = _wallet.account?.label;
}
return true;
});
}
@observable
@@ -170,6 +200,10 @@ abstract class DashboardViewModelBase with Store {
ReactionDisposer _reaction;
ReactionDisposer _onMoneroAccountChangeReaction;
ReactionDisposer _onMoneroBalanceChangeReaction;
Future<void> reconnect() async {
final node = appStore.settingsStore.getCurrentNode(wallet.type);
await wallet.connectToNode(node: node);
@@ -180,9 +214,47 @@ abstract class DashboardViewModelBase with Store {
this.wallet = wallet;
type = wallet.type;
name = wallet.name;
if (wallet is MoneroWallet) {
subname = wallet.account?.label;
_onMoneroAccountChangeReaction?.reaction?.dispose();
_onMoneroBalanceChangeReaction?.reaction?.dispose();
_onMoneroAccountChangeReaction = reaction((_) => wallet.account,
(Account account) => _onMoneroAccountChange(wallet));
_onMoneroBalanceChangeReaction = reaction((_) => wallet.balance,
(MoneroBalance balance) => _onMoneroTransactionsUpdate(wallet));
_onMoneroTransactionsUpdate(wallet);
} else {
transactions.clear();
transactions.addAll(wallet.transactionHistory.transactions.values.map(
(transaction) => TransactionListItem(
transaction: transaction,
balanceViewModel: balanceViewModel,
settingsStore: appStore.settingsStore)));
}
}
@action
void _onMoneroAccountChange(MoneroWallet wallet) {
subname = wallet.account?.label;
_onMoneroTransactionsUpdate(wallet);
}
@action
void _onMoneroTransactionsUpdate(MoneroWallet wallet) {
transactions.clear();
transactions.addAll(wallet.transactionHistory.transactions.values.map(
(transaction) => TransactionListItem(
final _accountTransactions = wallet.transactionHistory.transactions.values
.where((tx) => tx.accountIndex == wallet.account.id)
.toList();
transactions.addAll(_accountTransactions.map((transaction) =>
TransactionListItem(
transaction: transaction,
balanceViewModel: balanceViewModel,
settingsStore: appStore.settingsStore)));
@@ -174,7 +174,7 @@ abstract class ExchangeViewModelBase with Store {
return;
}
final _amount = double.parse(amount.replaceAll(',', '.'));
final _amount = double.parse(amount.replaceAll(',', '.')) ?? 0;
provider
.calculateAmount(
from: depositCurrency,
@@ -247,10 +247,12 @@ abstract class ExchangeViewModelBase with Store {
if (limitsState is LimitsLoadedSuccessfully && amount != null) {
if (double.parse(amount) < limits.min) {
tradeState = TradeIsCreatedFailure(
title: provider.title,
error: S.current.error_text_minimal_limit('${provider.description}',
'${limits.min}', currency.toString()));
} else if (limits.max != null && double.parse(amount) > limits.max) {
tradeState = TradeIsCreatedFailure(
title: provider.title,
error: S.current.error_text_maximum_limit('${provider.description}',
'${limits.max}', currency.toString()));
} else {
@@ -262,11 +264,14 @@ abstract class ExchangeViewModelBase with Store {
await trades.add(trade);
tradeState = TradeIsCreatedSuccessfully(trade: trade);
} catch (e) {
tradeState = TradeIsCreatedFailure(error: e.toString());
tradeState = TradeIsCreatedFailure(
title: provider.title,
error: e.toString());
}
}
} else {
tradeState = TradeIsCreatedFailure(
title: provider.title,
error: S.current
.error_text_limits_loading_failed('${provider.description}'));
}
@@ -13,6 +13,7 @@ abstract class MoneroAccountEditOrCreateViewModelBase with Store {
{AccountListItem accountListItem})
: state = InitialExecutionState(),
isEdit = accountListItem != null,
label = accountListItem?.label??'',
_accountListItem = accountListItem;
final bool isEdit;
@@ -12,7 +12,9 @@ class NodeCreateOrEditViewModel = NodeCreateOrEditViewModelBase
abstract class NodeCreateOrEditViewModelBase with Store {
NodeCreateOrEditViewModelBase(this._nodeSource, this._wallet)
: state = InitialExecutionState();
: state = InitialExecutionState(),
connectionState = InitialExecutionState(),
useSSL = false;
@observable
ExecutionState state;
@@ -29,6 +31,12 @@ abstract class NodeCreateOrEditViewModelBase with Store {
@observable
String password;
@observable
ExecutionState connectionState;
@observable
bool useSSL;
@computed
bool get isReady =>
(address?.isNotEmpty ?? false) && (port?.isNotEmpty ?? false);
@@ -54,6 +62,7 @@ abstract class NodeCreateOrEditViewModelBase with Store {
port = '';
login = '';
password = '';
useSSL = false;
}
@action
@@ -61,11 +70,25 @@ abstract class NodeCreateOrEditViewModelBase with Store {
try {
state = IsExecutingState();
final node =
Node(uri: uri, type: _wallet.type, login: login, password: password);
Node(uri: uri, type: _wallet.type, login: login, password: password,
useSSL: useSSL);
await _nodeSource.add(node);
state = ExecutedSuccessfullyState();
} catch (e) {
state = FailureState(e.toString());
}
}
@action
Future<void> connect() async {
try {
connectionState = IsExecutingState();
final node =
Node(uri: uri, type: _wallet.type, login: login, password: password);
final isAlive = await node.requestNode();
connectionState = ExecutedSuccessfullyState(payload: isAlive);
} catch (e) {
connectionState = FailureState(e.toString());
}
}
}
@@ -2,10 +2,11 @@ import 'package:flutter/foundation.dart';
import 'package:cake_wallet/utils/list_item.dart';
class WalletAddressListItem extends ListItem {
const WalletAddressListItem({@required this.address, this.name, this.id})
: super();
const WalletAddressListItem({@required this.address, @required this.isPrimary,
this.name, this.id}) : super();
final int id;
final bool isPrimary;
final String address;
final String name;
@@ -97,16 +97,28 @@ abstract class WalletAddressListViewModelBase with Store {
final addressList = ObservableList<ListItem>();
if (wallet is MoneroWallet) {
addressList.addAll(wallet.subaddressList.subaddresses.map((subaddress) =>
WalletAddressListItem(
id: subaddress.id,
name: subaddress.label,
address: subaddress.address)));
final primaryAddress = wallet.subaddressList.subaddresses.first;
addressList.addAll(wallet.subaddressList.subaddresses.map((subaddress) {
final isPrimary = subaddress == primaryAddress;
return WalletAddressListItem(
id: subaddress.id,
isPrimary: isPrimary,
name: subaddress.label,
address: subaddress.address);
}));
}
if (wallet is BitcoinWallet) {
final bitcoinAddresses = wallet.addresses.map((addr) =>
WalletAddressListItem(name: addr.label, address: addr.address));
final primaryAddress = wallet.addresses.first;
final bitcoinAddresses = wallet.addresses.map((addr) {
final isPrimary = addr == primaryAddress;
return WalletAddressListItem(
isPrimary: isPrimary,
name: addr.label,
address: addr.address);
});
addressList.addAll(bitcoinAddresses);
}