mirror of
https://github.com/Such-Software/hash-wallet.git
synced 2026-10-18 05:51:34 +02:00
General changes. CAKE-1.
This commit is contained in:
@@ -3,4 +3,7 @@ import 'package:cake_wallet/src/domain/common/crypto_amount_format.dart';
|
||||
const bitcoinAmountDivider = 100000000;
|
||||
|
||||
double bitcoinAmountToDouble({int amount}) =>
|
||||
cryptoAmountToDouble(amount: amount, divider: bitcoinAmountDivider);
|
||||
cryptoAmountToDouble(amount: amount, divider: bitcoinAmountDivider);
|
||||
|
||||
int doubleToBitcoinAmount(double amount) =>
|
||||
(amount * bitcoinAmountDivider).toInt();
|
||||
|
||||
@@ -22,6 +22,12 @@ class Contact extends HiveObject {
|
||||
|
||||
CryptoCurrency get type => CryptoCurrency.deserialize(raw: raw);
|
||||
|
||||
@override
|
||||
bool operator ==(Object o) => o is Contact && o.key == key;
|
||||
|
||||
@override
|
||||
int get hashCode => key.hashCode;
|
||||
|
||||
void updateCryptoCurrency({@required CryptoCurrency currency}) =>
|
||||
raw = currency.raw;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import 'package:cake_wallet/src/domain/common/wallet_type.dart';
|
||||
import 'package:cake_wallet/store/settings_store.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
@@ -14,7 +16,6 @@ Future defaultSettingsMigration(
|
||||
final currentVersion =
|
||||
sharedPreferences.getInt('current_default_settings_migration_version') ??
|
||||
0;
|
||||
|
||||
if (currentVersion >= version) {
|
||||
return;
|
||||
}
|
||||
@@ -28,10 +29,11 @@ Future defaultSettingsMigration(
|
||||
switch (version) {
|
||||
case 1:
|
||||
await sharedPreferences.setString(
|
||||
'current_fiat_currency', FiatCurrency.usd.toString());
|
||||
SettingsStoreBase.currentFiatCurrencyKey, FiatCurrency.usd.toString());
|
||||
await sharedPreferences.setInt(
|
||||
'current_fee_priority', TransactionPriority.standart.raw);
|
||||
await sharedPreferences.setInt('current_balance_display_mode',
|
||||
SettingsStoreBase.currentTransactionPriorityKey, TransactionPriority.standart.raw);
|
||||
await sharedPreferences.setInt(
|
||||
SettingsStoreBase.currentBalanceDisplayModeKey,
|
||||
BalanceDisplayMode.availableBalance.raw);
|
||||
await sharedPreferences.setBool('save_recipient_address', true);
|
||||
await resetToDefault(nodes);
|
||||
@@ -44,6 +46,10 @@ Future defaultSettingsMigration(
|
||||
await replaceDefaultNode(
|
||||
sharedPreferences: sharedPreferences, nodes: nodes);
|
||||
|
||||
break;
|
||||
case 3:
|
||||
await updateNodeTypes(nodes: nodes);
|
||||
await addBitcoinElectrumServerList(nodes: nodes);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -87,9 +93,11 @@ Future<void> changeCurrentNodeToDefault(
|
||||
final timeZone = DateTime.now().timeZoneOffset.inHours;
|
||||
String nodeUri = '';
|
||||
|
||||
if (timeZone >= 1) { // Eurasia
|
||||
if (timeZone >= 1) {
|
||||
// Eurasia
|
||||
nodeUri = 'xmr-node-eu.cakewallet.com:18081';
|
||||
} else if (timeZone <= -4) { // America
|
||||
} else if (timeZone <= -4) {
|
||||
// America
|
||||
nodeUri = 'xmr-node-usa-east.cakewallet.com:18081';
|
||||
}
|
||||
|
||||
@@ -121,3 +129,17 @@ Future<void> replaceDefaultNode(
|
||||
await changeCurrentNodeToDefault(
|
||||
sharedPreferences: sharedPreferences, nodes: nodes);
|
||||
}
|
||||
|
||||
Future<void> updateNodeTypes({@required Box<Node> nodes}) async {
|
||||
nodes.values.forEach((node) async {
|
||||
if (node.type == null) {
|
||||
node.type = WalletType.monero;
|
||||
await node.save();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> addBitcoinElectrumServerList({@required Box<Node> nodes}) async {
|
||||
final serverList = await loadElectrumServerList();
|
||||
await nodes.addAll(serverList);
|
||||
}
|
||||
|
||||
@@ -2,18 +2,22 @@ import 'package:flutter/foundation.dart';
|
||||
import 'dart:convert';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:cake_wallet/src/domain/common/wallet_type.dart';
|
||||
import 'package:cake_wallet/src/domain/common/digest_request.dart';
|
||||
|
||||
part 'node.g.dart';
|
||||
|
||||
@HiveType(typeId: 1)
|
||||
class Node extends HiveObject {
|
||||
Node({@required this.uri, this.login, this.password});
|
||||
Node({@required this.uri, @required WalletType type, this.login, this.password}) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
Node.fromMap(Map map)
|
||||
: uri = (map['uri'] ?? '') as String,
|
||||
login = map['login'] as String,
|
||||
password = map['password'] as String;
|
||||
password = map['password'] as String,
|
||||
typeRaw = map['typeRaw'] as int;
|
||||
|
||||
static const boxName = 'Nodes';
|
||||
|
||||
@@ -26,7 +30,29 @@ class Node extends HiveObject {
|
||||
@HiveField(2)
|
||||
String password;
|
||||
|
||||
Future<bool> requestNode(String uri, {String login, String password}) async {
|
||||
@HiveField(3)
|
||||
int typeRaw;
|
||||
|
||||
WalletType get type => deserializeFromInt(typeRaw);
|
||||
|
||||
set type(WalletType type) => typeRaw = serializeToInt(type);
|
||||
|
||||
Future<bool> requestNode() async {
|
||||
try {
|
||||
switch (type) {
|
||||
case WalletType.monero:
|
||||
return requestMoneroNode();
|
||||
case WalletType.bitcoin:
|
||||
return requestBitcoinElectrumServer();
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
} catch (_) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> requestMoneroNode() async {
|
||||
Map<String, dynamic> resBody;
|
||||
|
||||
if (login != null && password != null) {
|
||||
@@ -38,12 +64,17 @@ class Node extends HiveObject {
|
||||
final url = Uri.http(uri, '/json_rpc');
|
||||
final headers = {'Content-type': 'application/json'};
|
||||
final body =
|
||||
json.encode({"jsonrpc": "2.0", "id": "0", "method": "get_info"});
|
||||
json.encode({'jsonrpc': '2.0', 'id': '0', 'method': 'get_info'});
|
||||
final response =
|
||||
await http.post(url.toString(), headers: headers, body: body);
|
||||
resBody = json.decode(response.body) as Map<String, dynamic>;
|
||||
}
|
||||
|
||||
return !(resBody["result"]["offline"] as bool);
|
||||
return !(resBody['result']['offline'] as bool);
|
||||
}
|
||||
|
||||
Future<bool> requestBitcoinElectrumServer() async {
|
||||
// FIXME: IMPLEMENT ME
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import 'package:flutter/services.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import "package:yaml/yaml.dart";
|
||||
import 'package:cake_wallet/src/domain/common/node.dart';
|
||||
import 'package:cake_wallet/src/domain/common/wallet_type.dart';
|
||||
|
||||
Future<List<Node>> loadDefaultNodes() async {
|
||||
final nodesRaw = await rootBundle.loadString('assets/node_list.yml');
|
||||
@@ -16,15 +17,34 @@ Future<List<Node>> loadDefaultNodes() async {
|
||||
}).toList();
|
||||
}
|
||||
|
||||
Future<List<Node>> loadElectrumServerList() async {
|
||||
final serverListRaw =
|
||||
await rootBundle.loadString('assets/electrum_server_list.yml');
|
||||
final serverList = loadYaml(serverListRaw) as YamlList;
|
||||
|
||||
return serverList.map((dynamic raw) {
|
||||
if (raw is Map) {
|
||||
final node = Node.fromMap(raw);
|
||||
node?.type = WalletType.bitcoin;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
return null;
|
||||
}).toList();
|
||||
}
|
||||
|
||||
Future resetToDefault(Box<Node> nodeSource) async {
|
||||
final nodes = await loadDefaultNodes();
|
||||
final enteties = Map<int, Node>();
|
||||
final moneroNodes = await loadDefaultNodes();
|
||||
final bitcoinElectrumServerList = await loadElectrumServerList();
|
||||
final nodes = moneroNodes + bitcoinElectrumServerList;
|
||||
final entities = <int, Node>{};
|
||||
|
||||
await nodeSource.clear();
|
||||
|
||||
for (var i = 0; i < nodes.length; i++) {
|
||||
enteties[i] = nodes[i];
|
||||
entities[i] = nodes[i];
|
||||
}
|
||||
|
||||
await nodeSource.putAll(enteties);
|
||||
await nodeSource.putAll(entities);
|
||||
}
|
||||
|
||||
@@ -20,15 +20,19 @@ int serializeToInt(WalletType type) {
|
||||
switch (type) {
|
||||
case WalletType.monero:
|
||||
return 0;
|
||||
case WalletType.bitcoin:
|
||||
return 1;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
WalletType deserializeToInt(int raw) {
|
||||
WalletType deserializeFromInt(int raw) {
|
||||
switch (raw) {
|
||||
case 0:
|
||||
return WalletType.monero;
|
||||
case 1:
|
||||
return WalletType.bitcoin;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ void setReactions(
|
||||
@required SyncStore syncStore,
|
||||
@required WalletStore walletStore,
|
||||
@required WalletService walletService,
|
||||
@required AuthenticationStore authenticationStore,
|
||||
// @required AuthenticationStore authenticationStore,
|
||||
@required LoginStore loginStore}) {
|
||||
connectToNode(settingsStore: settingsStore, walletStore: walletStore);
|
||||
onSyncStatusChange(
|
||||
|
||||
@@ -1,109 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/stores/account_list/account_list_store.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/src/domain/monero/account.dart';
|
||||
import 'package:cake_wallet/palette.dart';
|
||||
import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
|
||||
|
||||
class AccountPage extends BasePage {
|
||||
AccountPage({this.account});
|
||||
|
||||
final Account account;
|
||||
|
||||
@override
|
||||
String get title => S.current.account;
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) => AccountForm(account);
|
||||
}
|
||||
|
||||
class AccountForm extends StatefulWidget {
|
||||
AccountForm(this.account);
|
||||
|
||||
final Account account;
|
||||
|
||||
@override
|
||||
AccountFormState createState() => AccountFormState();
|
||||
}
|
||||
|
||||
class AccountFormState extends State<AccountForm> {
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
final _textController = TextEditingController();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
if (widget.account != null) _textController.text = widget.account.label;
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_textController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final accountListStore = Provider.of<AccountListStore>(context);
|
||||
|
||||
_textController.addListener(() {
|
||||
if (_textController.text.isNotEmpty) {
|
||||
accountListStore.setDisabledStatus(false);
|
||||
} else {
|
||||
accountListStore.setDisabledStatus(true);
|
||||
}
|
||||
});
|
||||
|
||||
return Form(
|
||||
key: _formKey,
|
||||
child: Container(
|
||||
color: Theme.of(context).backgroundColor,
|
||||
padding: EdgeInsets.all(24.0),
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: Center(
|
||||
child: BaseTextFormField(
|
||||
controller: _textController,
|
||||
hintText: S.of(context).account,
|
||||
validator: (value) {
|
||||
accountListStore.validateAccountName(value);
|
||||
return accountListStore.errorMessage;
|
||||
},
|
||||
)
|
||||
)),
|
||||
Observer(
|
||||
builder: (_) => LoadingPrimaryButton(
|
||||
onPressed: () async {
|
||||
if (!_formKey.currentState.validate()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (widget.account != null) {
|
||||
await accountListStore.renameAccount(
|
||||
index: widget.account.id,
|
||||
label: _textController.text);
|
||||
} else {
|
||||
await accountListStore.addAccount(
|
||||
label: _textController.text);
|
||||
}
|
||||
Navigator.of(context).pop(_textController.text);
|
||||
},
|
||||
text:
|
||||
widget.account != null ? S.of(context).rename : S.of(context).add,
|
||||
color: Colors.green,
|
||||
textColor: Colors.white,
|
||||
isLoading: accountListStore.isAccountCreating,
|
||||
isDisabled: accountListStore.isDisabledStatus,
|
||||
))
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,293 +0,0 @@
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:flutter_slidable/flutter_slidable.dart';
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/domain/common/crypto_currency.dart';
|
||||
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/widgets/alert_with_two_actions.dart';
|
||||
|
||||
class AddressBookPage extends BasePage {
|
||||
AddressBookPage({this.isEditable = true});
|
||||
|
||||
final bool isEditable;
|
||||
|
||||
@override
|
||||
String get title => S.current.address_book;
|
||||
|
||||
@override
|
||||
Widget trailing(BuildContext context) {
|
||||
if (!isEditable) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final addressBookStore = Provider.of<AddressBookStore>(context);
|
||||
|
||||
return Container(
|
||||
width: 32.0,
|
||||
height: 32.0,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: Theme.of(context).accentTextTheme.title.backgroundColor
|
||||
),
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
children: <Widget>[
|
||||
Icon(Icons.add,
|
||||
color: Theme.of(context).primaryTextTheme.title.color,
|
||||
size: 22.0),
|
||||
ButtonTheme(
|
||||
minWidth: 32.0,
|
||||
height: 32.0,
|
||||
child: FlatButton(
|
||||
shape: CircleBorder(),
|
||||
onPressed: () async {
|
||||
await Navigator.of(context)
|
||||
.pushNamed(Routes.addressBookAddContact);
|
||||
await addressBookStore.updateContactList();
|
||||
},
|
||||
child: Offstage()),
|
||||
)
|
||||
],
|
||||
));
|
||||
}
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) {
|
||||
final addressBookStore = Provider.of<AddressBookStore>(context);
|
||||
|
||||
return Container(
|
||||
color: Theme.of(context).backgroundColor,
|
||||
padding: EdgeInsets.only(top: 20.0, bottom: 20.0),
|
||||
child: Observer(
|
||||
builder: (_) => ListView.separated(
|
||||
separatorBuilder: (_, __) => Container(
|
||||
height: 1,
|
||||
padding: EdgeInsets.only(left: 24),
|
||||
color: Theme.of(context).accentTextTheme.title.backgroundColor,
|
||||
child: Container(
|
||||
height: 1,
|
||||
color: Theme.of(context).dividerColor,
|
||||
),
|
||||
),
|
||||
itemCount: addressBookStore.contactList == null
|
||||
? 0
|
||||
: addressBookStore.contactList.length,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
final contact = addressBookStore.contactList[index];
|
||||
final image = _getCurrencyImage(contact.type);
|
||||
|
||||
final isDrawTop = index == 0 ? true : false;
|
||||
final isDrawBottom = index == addressBookStore.contactList.length - 1 ? true : false;
|
||||
|
||||
final content = GestureDetector(
|
||||
onTap: () async {
|
||||
if (!isEditable) {
|
||||
Navigator.of(context).pop(contact);
|
||||
return;
|
||||
}
|
||||
|
||||
final isCopied = await showNameAndAddressDialog(
|
||||
context, contact.name, contact.address);
|
||||
|
||||
if (isCopied != null && isCopied) {
|
||||
await Clipboard.setData(
|
||||
ClipboardData(text: contact.address));
|
||||
Scaffold.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
S.of(context).copied_to_clipboard,
|
||||
style: TextStyle(
|
||||
color: Colors.white
|
||||
),
|
||||
),
|
||||
backgroundColor: Colors.green,
|
||||
duration: Duration(milliseconds: 1500),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
isDrawTop
|
||||
? Container(
|
||||
width: double.infinity,
|
||||
height: 1,
|
||||
color: Theme.of(context).dividerColor,
|
||||
)
|
||||
: Offstage(),
|
||||
Container(
|
||||
width: double.infinity,
|
||||
color: Theme.of(context).accentTextTheme.title.backgroundColor,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(left: 24, top: 16, bottom: 16, right: 24),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
image != null
|
||||
? image
|
||||
: Offstage(),
|
||||
Padding(
|
||||
padding: image != null
|
||||
? EdgeInsets.only(left: 12)
|
||||
: EdgeInsets.only(left: 0),
|
||||
child: Text(
|
||||
contact.name,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: Theme.of(context).primaryTextTheme.title.color
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
)
|
||||
),
|
||||
),
|
||||
isDrawBottom
|
||||
? Container(
|
||||
width: double.infinity,
|
||||
height: 1,
|
||||
color: Theme.of(context).dividerColor,
|
||||
)
|
||||
: Offstage(),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
return !isEditable
|
||||
? content
|
||||
: Slidable(
|
||||
key: Key('${contact.key}'),
|
||||
actionPane: SlidableDrawerActionPane(),
|
||||
child: content,
|
||||
secondaryActions: <Widget>[
|
||||
IconSlideAction(
|
||||
caption: S.of(context).edit,
|
||||
color: Colors.blue,
|
||||
icon: Icons.edit,
|
||||
onTap: () async {
|
||||
await Navigator.of(context).pushNamed(
|
||||
Routes.addressBookAddContact,
|
||||
arguments: contact);
|
||||
await addressBookStore.updateContactList();
|
||||
},
|
||||
),
|
||||
IconSlideAction(
|
||||
caption: S.of(context).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();
|
||||
}
|
||||
});
|
||||
},
|
||||
),
|
||||
],
|
||||
dismissal: SlidableDismissal(
|
||||
child: SlidableDrawerDismissal(),
|
||||
onDismissed: (actionType) async {
|
||||
await addressBookStore.delete(contact: contact);
|
||||
await addressBookStore.updateContactList();
|
||||
},
|
||||
onWillDismiss: (actionType) async {
|
||||
return await showAlertDialog(context);
|
||||
},
|
||||
),
|
||||
);
|
||||
}),
|
||||
));
|
||||
}
|
||||
|
||||
Image _getCurrencyImage(CryptoCurrency currency) {
|
||||
Image image;
|
||||
switch (currency) {
|
||||
case CryptoCurrency.xmr:
|
||||
image = Image.asset('assets/images/monero.png', height: 24, width: 24);
|
||||
break;
|
||||
case CryptoCurrency.ada:
|
||||
image = Image.asset('assets/images/ada.png', height: 24, width: 24);
|
||||
break;
|
||||
case CryptoCurrency.bch:
|
||||
image = Image.asset('assets/images/bch.png', height: 24, width: 24);
|
||||
break;
|
||||
case CryptoCurrency.bnb:
|
||||
image = Image.asset('assets/images/bnb.png', height: 24, width: 24);
|
||||
break;
|
||||
case CryptoCurrency.btc:
|
||||
image = Image.asset('assets/images/bitcoin.png', height: 24, width: 24);
|
||||
break;
|
||||
case CryptoCurrency.dash:
|
||||
image = Image.asset('assets/images/dash.png', height: 24, width: 24);
|
||||
break;
|
||||
case CryptoCurrency.eos:
|
||||
image = Image.asset('assets/images/eos.png', height: 24, width: 24);
|
||||
break;
|
||||
case CryptoCurrency.eth:
|
||||
image = Image.asset('assets/images/eth.png', height: 24, width: 24);
|
||||
break;
|
||||
case CryptoCurrency.ltc:
|
||||
image = Image.asset('assets/images/litecoin.png', height: 24, width: 24);
|
||||
break;
|
||||
case CryptoCurrency.nano:
|
||||
image = Image.asset('assets/images/nano.png', height: 24, width: 24);
|
||||
break;
|
||||
case CryptoCurrency.trx:
|
||||
image = Image.asset('assets/images/trx.png', height: 24, width: 24);
|
||||
break;
|
||||
case CryptoCurrency.usdt:
|
||||
image = Image.asset('assets/images/usdt.png', height: 24, width: 24);
|
||||
break;
|
||||
case CryptoCurrency.xlm:
|
||||
image = Image.asset('assets/images/xlm.png', height: 24, width: 24);
|
||||
break;
|
||||
case CryptoCurrency.xrp:
|
||||
image = Image.asset('assets/images/xrp.png', height: 24, width: 24);
|
||||
break;
|
||||
default:
|
||||
image = null;
|
||||
}
|
||||
return image;
|
||||
}
|
||||
|
||||
Future<bool> showAlertDialog(BuildContext context) async {
|
||||
return await showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertWithTwoActions(
|
||||
alertTitle: S.of(context).address_remove_contact,
|
||||
alertContent: S.of(context).address_remove_content,
|
||||
leftButtonText: S.of(context).remove,
|
||||
rightButtonText: S.of(context).cancel,
|
||||
actionLeftButton: () => Navigator.of(context).pop(true),
|
||||
actionRightButton: () => Navigator.of(context).pop(false)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Future<bool> showNameAndAddressDialog(
|
||||
BuildContext context, String name, String address) async {
|
||||
return await showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertWithTwoActions(
|
||||
alertTitle: name,
|
||||
alertContent: address,
|
||||
leftButtonText: S.of(context).copy,
|
||||
rightButtonText: S.of(context).cancel,
|
||||
actionLeftButton: () => Navigator.of(context).pop(true),
|
||||
actionRightButton: () => Navigator.of(context).pop(false)
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,234 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/domain/common/crypto_currency.dart';
|
||||
import 'package:cake_wallet/src/domain/common/contact.dart';
|
||||
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/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/src/widgets/address_text_field.dart';
|
||||
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
|
||||
import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
|
||||
import 'package:cake_wallet/src/screens/exchange/widgets/currency_picker.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
|
||||
|
||||
class ContactPage extends BasePage {
|
||||
ContactPage({this.contact});
|
||||
|
||||
final Contact contact;
|
||||
|
||||
@override
|
||||
String get title => S.current.contact;
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) => ContactForm(contact);
|
||||
}
|
||||
|
||||
class ContactForm extends StatefulWidget {
|
||||
ContactForm(this.contact);
|
||||
|
||||
final Contact contact;
|
||||
|
||||
@override
|
||||
State<ContactForm> createState() => ContactFormState();
|
||||
}
|
||||
|
||||
class ContactFormState extends State<ContactForm> {
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
final _contactNameController = TextEditingController();
|
||||
final _currencyTypeController = TextEditingController();
|
||||
final _addressController = TextEditingController();
|
||||
final currencies = CryptoCurrency.all;
|
||||
|
||||
CryptoCurrency _selectectCrypto;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
if (widget.contact != null) {
|
||||
_selectectCrypto = widget.contact.type;
|
||||
_contactNameController.text = widget.contact.name;
|
||||
_currencyTypeController.text = _selectectCrypto.toString();
|
||||
_addressController.text = widget.contact.address;
|
||||
WidgetsBinding.instance.addPostFrameCallback(afterLayout);
|
||||
}
|
||||
}
|
||||
|
||||
void afterLayout(dynamic _) {
|
||||
final addressBookStore = Provider.of<AddressBookStore>(context);
|
||||
addressBookStore.setDisabledStatus(false);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_contactNameController.dispose();
|
||||
_currencyTypeController.dispose();
|
||||
_addressController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void onHandleControllers(AddressBookStore addressBookStore) {
|
||||
if (_contactNameController.text.isNotEmpty &&
|
||||
_addressController.text.isNotEmpty &&
|
||||
_currencyTypeController.text.isNotEmpty) {
|
||||
addressBookStore.setDisabledStatus(false);
|
||||
} else {
|
||||
addressBookStore.setDisabledStatus(true);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final addressBookStore = Provider.of<AddressBookStore>(context);
|
||||
final downArrow = Image.asset(
|
||||
'assets/images/arrow_bottom_purple_icon.png',
|
||||
color: Theme.of(context).dividerColor,
|
||||
height: 8);
|
||||
|
||||
_contactNameController.addListener(() {onHandleControllers(addressBookStore);});
|
||||
_currencyTypeController.addListener(() {onHandleControllers(addressBookStore);});
|
||||
_addressController.addListener(() {onHandleControllers(addressBookStore);});
|
||||
|
||||
return Container(
|
||||
color: Theme.of(context).backgroundColor,
|
||||
child: ScrollableWithBottomSection(
|
||||
contentPadding: EdgeInsets.all(24),
|
||||
content: Form(
|
||||
key: _formKey,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
BaseTextFormField(
|
||||
controller: _contactNameController,
|
||||
hintText: S.of(context).contact_name,
|
||||
validator: (value) {
|
||||
addressBookStore.validateContactName(value);
|
||||
return addressBookStore.errorMessage;
|
||||
}
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 20),
|
||||
child: Container(
|
||||
child: InkWell(
|
||||
onTap: () => _presentPicker(context),
|
||||
child: IgnorePointer(
|
||||
child: BaseTextFormField(
|
||||
controller: _currencyTypeController,
|
||||
hintText: S.of(context).settings_currency,
|
||||
suffixIcon: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: <Widget>[
|
||||
downArrow
|
||||
],
|
||||
),
|
||||
)
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 20),
|
||||
child: AddressTextField(
|
||||
controller: _addressController,
|
||||
options: [AddressTextFieldOption.qrCode],
|
||||
validator: (value) {
|
||||
addressBookStore.validateAddress(value,
|
||||
cryptoCurrency: _selectectCrypto);
|
||||
return addressBookStore.errorMessage;
|
||||
},
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
bottomSectionPadding: EdgeInsets.only(
|
||||
left: 24,
|
||||
right: 24,
|
||||
bottom: 24
|
||||
),
|
||||
bottomSection: Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: PrimaryButton(
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
_selectectCrypto = null;
|
||||
_contactNameController.text = '';
|
||||
_currencyTypeController.text = '';
|
||||
_addressController.text = '';
|
||||
});
|
||||
},
|
||||
text: S.of(context).reset,
|
||||
color: Colors.red,
|
||||
textColor: Colors.white
|
||||
),
|
||||
),
|
||||
SizedBox(width: 20),
|
||||
Expanded(
|
||||
child: Observer(
|
||||
builder: (_) => PrimaryButton(
|
||||
onPressed: () async {
|
||||
if (!_formKey.currentState.validate()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (widget.contact == null) {
|
||||
final newContact = Contact(
|
||||
name: _contactNameController.text,
|
||||
address: _addressController.text,
|
||||
type: _selectectCrypto);
|
||||
|
||||
await addressBookStore.add(contact: newContact);
|
||||
} else {
|
||||
widget.contact.name = _contactNameController.text;
|
||||
widget.contact.address = _addressController.text;
|
||||
widget.contact
|
||||
.updateCryptoCurrency(currency: _selectectCrypto);
|
||||
|
||||
await addressBookStore.update(
|
||||
contact: widget.contact);
|
||||
}
|
||||
Navigator.pop(context);
|
||||
} catch (e) {
|
||||
await showDialog<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertWithOneAction(
|
||||
alertTitle: S.current.contact,
|
||||
alertContent: e.toString(),
|
||||
buttonText: S.of(context).ok,
|
||||
buttonAction: () => Navigator.of(context).pop()
|
||||
);
|
||||
});
|
||||
}
|
||||
},
|
||||
text: S.of(context).save,
|
||||
color: Colors.green,
|
||||
textColor: Colors.white,
|
||||
isDisabled: addressBookStore.isDisabledStatus,
|
||||
)
|
||||
)
|
||||
)
|
||||
],
|
||||
)),
|
||||
);
|
||||
}
|
||||
|
||||
void _presentPicker(BuildContext context) {
|
||||
showDialog<void>(
|
||||
builder: (_) => CurrencyPicker(
|
||||
selectedAtIndex: currencies.indexOf(_selectectCrypto),
|
||||
items: currencies,
|
||||
title: S.of(context).please_select,
|
||||
onItemSelected: (CryptoCurrency item) {
|
||||
_selectectCrypto = item;
|
||||
_currencyTypeController.text = _selectectCrypto.toString();
|
||||
}
|
||||
),
|
||||
context: context);
|
||||
}
|
||||
}
|
||||
@@ -125,7 +125,7 @@ abstract class BasePage extends StatelessWidget {
|
||||
_isDarkTheme ? backgroundDarkColor : backgroundLightColor,
|
||||
resizeToAvoidBottomPadding: resizeToAvoidBottomPadding,
|
||||
appBar: appBar(context),
|
||||
body: SafeArea(child: body(context)),
|
||||
body: body(context), //SafeArea(child: ),
|
||||
floatingActionButton: floatingActionButton(context));
|
||||
|
||||
return rootWrapper?.call(context, root) ?? root;
|
||||
|
||||
@@ -0,0 +1,280 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:flutter_slidable/flutter_slidable.dart';
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/domain/common/crypto_currency.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
|
||||
import 'package:cake_wallet/view_model/contact_list/contact_list_view_model.dart';
|
||||
|
||||
class ContactListPage extends BasePage {
|
||||
ContactListPage(this.contactListViewModel, {this.isEditable = true});
|
||||
|
||||
final ContactListViewModel contactListViewModel;
|
||||
final bool isEditable;
|
||||
|
||||
@override
|
||||
String get title => S.current.address_book;
|
||||
|
||||
@override
|
||||
Widget trailing(BuildContext context) {
|
||||
if (!isEditable) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return Container(
|
||||
width: 32.0,
|
||||
height: 32.0,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: Theme.of(context).accentTextTheme.title.backgroundColor),
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
children: <Widget>[
|
||||
Icon(Icons.add,
|
||||
color: Theme.of(context).primaryTextTheme.title.color,
|
||||
size: 22.0),
|
||||
ButtonTheme(
|
||||
minWidth: 32.0,
|
||||
height: 32.0,
|
||||
child: FlatButton(
|
||||
shape: CircleBorder(),
|
||||
onPressed: () async {
|
||||
await Navigator.of(context)
|
||||
.pushNamed(Routes.addressBookAddContact);
|
||||
},
|
||||
child: Offstage()),
|
||||
)
|
||||
],
|
||||
));
|
||||
}
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) {
|
||||
return Container(
|
||||
color: Theme.of(context).backgroundColor,
|
||||
padding: EdgeInsets.only(top: 20.0, bottom: 20.0),
|
||||
child: Observer(
|
||||
builder: (_) {
|
||||
return contactListViewModel.contacts.isNotEmpty
|
||||
? ListView.separated(
|
||||
separatorBuilder: (_, __) => Container(
|
||||
height: 1,
|
||||
padding: EdgeInsets.only(left: 24),
|
||||
color: Theme.of(context)
|
||||
.accentTextTheme
|
||||
.title
|
||||
.backgroundColor,
|
||||
child: Container(
|
||||
height: 1,
|
||||
color: Theme.of(context).dividerColor,
|
||||
),
|
||||
),
|
||||
itemCount: contactListViewModel.contacts.length,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
final contact = contactListViewModel.contacts[index];
|
||||
final image = _getCurrencyImage(contact.type);
|
||||
final content = GestureDetector(
|
||||
onTap: () async {
|
||||
if (!isEditable) {
|
||||
Navigator.of(context).pop(contact);
|
||||
return;
|
||||
}
|
||||
|
||||
final isCopied = await showNameAndAddressDialog(
|
||||
context, contact.name, contact.address);
|
||||
|
||||
if (isCopied != null && isCopied) {
|
||||
await Clipboard.setData(
|
||||
ClipboardData(text: contact.address));
|
||||
Scaffold.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
S.of(context).copied_to_clipboard,
|
||||
style: TextStyle(color: Colors.white),
|
||||
),
|
||||
backgroundColor: Colors.green,
|
||||
duration: Duration(milliseconds: 1500),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
Container(
|
||||
width: double.infinity,
|
||||
color: Theme.of(context)
|
||||
.accentTextTheme
|
||||
.title
|
||||
.backgroundColor,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 24, top: 16, bottom: 16, right: 24),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
image ?? Offstage(),
|
||||
Padding(
|
||||
padding: image != null
|
||||
? EdgeInsets.only(left: 12)
|
||||
: EdgeInsets.only(left: 0),
|
||||
child: Text(
|
||||
contact.name,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.title
|
||||
.color),
|
||||
),
|
||||
)
|
||||
],
|
||||
)),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
return !isEditable
|
||||
? content
|
||||
: Slidable(
|
||||
key: Key('${contact.key}'),
|
||||
actionPane: SlidableDrawerActionPane(),
|
||||
child: content,
|
||||
secondaryActions: <Widget>[
|
||||
IconSlideAction(
|
||||
caption: S.of(context).edit,
|
||||
color: Colors.blue,
|
||||
icon: Icons.edit,
|
||||
onTap: () async => await Navigator.of(context)
|
||||
.pushNamed(Routes.addressBookAddContact,
|
||||
arguments: contact),
|
||||
),
|
||||
IconSlideAction(
|
||||
caption: S.of(context).delete,
|
||||
color: Colors.red,
|
||||
icon: CupertinoIcons.delete,
|
||||
onTap: () async {
|
||||
final isDelete =
|
||||
await showAlertDialog(context) ?? false;
|
||||
|
||||
if (isDelete) {
|
||||
await contactListViewModel
|
||||
.delete(contact);
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
dismissal: SlidableDismissal(
|
||||
child: SlidableDrawerDismissal(),
|
||||
onDismissed: (actionType) async =>
|
||||
await contactListViewModel.delete(contact),
|
||||
onWillDismiss: (actionType) async =>
|
||||
showAlertDialog(context),
|
||||
),
|
||||
);
|
||||
})
|
||||
: Center(
|
||||
child: Text(
|
||||
S.of(context).placeholder_contacts,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.caption
|
||||
.color
|
||||
.withOpacity(0.5),
|
||||
fontSize: 14),
|
||||
),
|
||||
);
|
||||
},
|
||||
));
|
||||
}
|
||||
|
||||
Image _getCurrencyImage(CryptoCurrency currency) {
|
||||
Image image;
|
||||
switch (currency) {
|
||||
case CryptoCurrency.xmr:
|
||||
image = Image.asset('assets/images/monero.png', height: 24, width: 24);
|
||||
break;
|
||||
case CryptoCurrency.ada:
|
||||
image = Image.asset('assets/images/ada.png', height: 24, width: 24);
|
||||
break;
|
||||
case CryptoCurrency.bch:
|
||||
image = Image.asset('assets/images/bch.png', height: 24, width: 24);
|
||||
break;
|
||||
case CryptoCurrency.bnb:
|
||||
image = Image.asset('assets/images/bnb.png', height: 24, width: 24);
|
||||
break;
|
||||
case CryptoCurrency.btc:
|
||||
image = Image.asset('assets/images/bitcoin.png', height: 24, width: 24);
|
||||
break;
|
||||
case CryptoCurrency.dash:
|
||||
image = Image.asset('assets/images/dash.png', height: 24, width: 24);
|
||||
break;
|
||||
case CryptoCurrency.eos:
|
||||
image = Image.asset('assets/images/eos.png', height: 24, width: 24);
|
||||
break;
|
||||
case CryptoCurrency.eth:
|
||||
image = Image.asset('assets/images/eth.png', height: 24, width: 24);
|
||||
break;
|
||||
case CryptoCurrency.ltc:
|
||||
image =
|
||||
Image.asset('assets/images/litecoin.png', height: 24, width: 24);
|
||||
break;
|
||||
case CryptoCurrency.nano:
|
||||
image = Image.asset('assets/images/nano.png', height: 24, width: 24);
|
||||
break;
|
||||
case CryptoCurrency.trx:
|
||||
image = Image.asset('assets/images/trx.png', height: 24, width: 24);
|
||||
break;
|
||||
case CryptoCurrency.usdt:
|
||||
image = Image.asset('assets/images/usdt.png', height: 24, width: 24);
|
||||
break;
|
||||
case CryptoCurrency.xlm:
|
||||
image = Image.asset('assets/images/xlm.png', height: 24, width: 24);
|
||||
break;
|
||||
case CryptoCurrency.xrp:
|
||||
image = Image.asset('assets/images/xrp.png', height: 24, width: 24);
|
||||
break;
|
||||
default:
|
||||
image = null;
|
||||
}
|
||||
return image;
|
||||
}
|
||||
|
||||
Future<bool> showAlertDialog(BuildContext context) async {
|
||||
return await showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertWithTwoActions(
|
||||
alertTitle: S.of(context).address_remove_contact,
|
||||
alertContent: S.of(context).address_remove_content,
|
||||
leftButtonText: S.of(context).remove,
|
||||
rightButtonText: S.of(context).cancel,
|
||||
actionLeftButton: () => Navigator.of(context).pop(true),
|
||||
actionRightButton: () => Navigator.of(context).pop(false));
|
||||
});
|
||||
}
|
||||
|
||||
Future<bool> showNameAndAddressDialog(
|
||||
BuildContext context, String name, String address) async {
|
||||
return await showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertWithTwoActions(
|
||||
alertTitle: name,
|
||||
alertContent: address,
|
||||
leftButtonText: S.of(context).copy,
|
||||
rightButtonText: S.of(context).cancel,
|
||||
actionLeftButton: () => Navigator.of(context).pop(true),
|
||||
actionRightButton: () => Navigator.of(context).pop(false));
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/core/address_validator.dart';
|
||||
import 'package:cake_wallet/core/contact_name_validator.dart';
|
||||
import 'package:cake_wallet/view_model/contact_list/contact_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/contact_list/contact_view_model_state.dart';
|
||||
import 'package:cake_wallet/src/domain/common/crypto_currency.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/src/widgets/address_text_field.dart';
|
||||
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
|
||||
import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
|
||||
import 'package:cake_wallet/src/screens/exchange/widgets/currency_picker.dart';
|
||||
|
||||
class ContactPage extends BasePage {
|
||||
ContactPage(this.contactViewModel)
|
||||
: _formKey = GlobalKey<FormState>(),
|
||||
_nameController = TextEditingController(),
|
||||
_addressController = TextEditingController(),
|
||||
_currencyTypeController = TextEditingController() {
|
||||
_nameController.text = contactViewModel.name;
|
||||
_addressController.text = contactViewModel.address;
|
||||
_nameController
|
||||
.addListener(() => contactViewModel.name = _nameController.text);
|
||||
_addressController
|
||||
.addListener(() => contactViewModel.address = _addressController.text);
|
||||
|
||||
autorun((_) =>
|
||||
_currencyTypeController.text = contactViewModel.currency.toString());
|
||||
}
|
||||
|
||||
@override
|
||||
String get title => S.current.contact;
|
||||
|
||||
final ContactViewModel contactViewModel;
|
||||
final GlobalKey<FormState> _formKey;
|
||||
final TextEditingController _nameController;
|
||||
final TextEditingController _currencyTypeController;
|
||||
final TextEditingController _addressController;
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) {
|
||||
final downArrow = Image.asset('assets/images/arrow_bottom_purple_icon.png',
|
||||
color: Theme.of(context).dividerColor, height: 8);
|
||||
|
||||
reaction((_) => contactViewModel.state, (ContactViewModelState state) {
|
||||
if (state is ContactCreationFailure) {
|
||||
_onContactSavingFailure(context, state.error);
|
||||
}
|
||||
|
||||
if (state is ContactSavingSuccessfully) {
|
||||
_onContactSavedSuccessfully(context);
|
||||
}
|
||||
});
|
||||
|
||||
return Container(
|
||||
color: Theme.of(context).backgroundColor,
|
||||
child: ScrollableWithBottomSection(
|
||||
contentPadding: EdgeInsets.all(24),
|
||||
content: Form(
|
||||
key: _formKey,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
BaseTextFormField(
|
||||
controller: _nameController,
|
||||
hintText: S.of(context).contact_name,
|
||||
validator: ContactNameValidator()),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 20),
|
||||
child: Container(
|
||||
child: InkWell(
|
||||
onTap: () => _presentCurrencyPicker(context),
|
||||
child: IgnorePointer(
|
||||
child: BaseTextFormField(
|
||||
controller: _currencyTypeController,
|
||||
hintText: S.of(context).settings_currency,
|
||||
suffixIcon: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: <Widget>[downArrow],
|
||||
),
|
||||
)),
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 20),
|
||||
child: Observer(
|
||||
builder: (_) => AddressTextField(
|
||||
controller: _addressController,
|
||||
options: [AddressTextFieldOption.qrCode],
|
||||
validator: AddressValidator(
|
||||
type: contactViewModel.currency),
|
||||
)),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
bottomSectionPadding:
|
||||
EdgeInsets.only(left: 24, right: 24, bottom: 24),
|
||||
bottomSection: Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: PrimaryButton(
|
||||
onPressed: () => contactViewModel.reset(),
|
||||
text: S.of(context).reset,
|
||||
color: Colors.red,
|
||||
textColor: Colors.white),
|
||||
),
|
||||
SizedBox(width: 20),
|
||||
Expanded(
|
||||
child: Observer(
|
||||
builder: (_) => PrimaryButton(
|
||||
onPressed: () async {
|
||||
if (!_formKey.currentState.validate()) {
|
||||
return;
|
||||
}
|
||||
|
||||
await contactViewModel.save();
|
||||
},
|
||||
text: S.of(context).save,
|
||||
color: Colors.green,
|
||||
textColor: Colors.white,
|
||||
isDisabled: !contactViewModel.isReady)))
|
||||
],
|
||||
)),
|
||||
);
|
||||
}
|
||||
|
||||
void _presentCurrencyPicker(BuildContext context) {
|
||||
showDialog<void>(
|
||||
builder: (_) => CurrencyPicker(
|
||||
selectedAtIndex:
|
||||
contactViewModel.currencies.indexOf(contactViewModel.currency),
|
||||
items: contactViewModel.currencies,
|
||||
title: S.of(context).please_select,
|
||||
onItemSelected: (CryptoCurrency item) =>
|
||||
contactViewModel.currency = item),
|
||||
context: context);
|
||||
}
|
||||
|
||||
void _onContactSavingFailure(BuildContext context, String error) {
|
||||
showDialog<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertWithOneAction(
|
||||
alertTitle: S.current.contact,
|
||||
alertContent: error,
|
||||
buttonText: S.of(context).ok,
|
||||
buttonAction: () => Navigator.of(context).pop());
|
||||
});
|
||||
}
|
||||
|
||||
void _onContactSavedSuccessfully(BuildContext context) =>
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
@@ -1,78 +1,150 @@
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:cake_wallet/view_model/dashboard_view_model.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/src/screens/dashboard/widgets/wallet_card.dart';
|
||||
import 'package:cake_wallet/src/screens/dashboard/widgets/trade_history_panel.dart';
|
||||
import 'package:cake_wallet/src/screens/dashboard/widgets/menu_widget.dart';
|
||||
|
||||
class DashboardPage extends StatelessWidget {
|
||||
class DashboardPage extends BasePage {
|
||||
DashboardPage({@required this.walletViewModel});
|
||||
|
||||
final DashboardViewModel walletViewModel;
|
||||
final _bodyKey = GlobalKey();
|
||||
@override
|
||||
Color get backgroundLightColor => Colors.transparent;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) =>
|
||||
DashboardPageBody(key: _bodyKey, walletViewModel: walletViewModel);
|
||||
}
|
||||
|
||||
class DashboardPageBody extends StatefulWidget {
|
||||
DashboardPageBody({Key key, @required this.walletViewModel})
|
||||
: super(key: key);
|
||||
|
||||
final DashboardViewModel walletViewModel;
|
||||
Color get backgroundDarkColor => Colors.transparent;
|
||||
|
||||
@override
|
||||
DashboardPageBodyState createState() => DashboardPageBodyState();
|
||||
}
|
||||
|
||||
class DashboardPageBodyState extends State<DashboardPageBody> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final menuButton = Image.asset(
|
||||
'assets/images/header.png',
|
||||
color: Theme.of(context).primaryTextTheme.title.color,
|
||||
);
|
||||
|
||||
return SafeArea(
|
||||
child: Scaffold(
|
||||
body: Container(
|
||||
Widget Function(BuildContext, Widget) get rootWrapper =>
|
||||
(BuildContext context, Widget scaffold) => Container(
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(colors: [
|
||||
Theme.of(context).scaffoldBackgroundColor,
|
||||
Theme.of(context).primaryColor
|
||||
], begin: Alignment.centerLeft, end: Alignment.centerRight)),
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
Container(
|
||||
padding: EdgeInsets.only(top: 10, right: 10),
|
||||
alignment: Alignment.centerRight,
|
||||
child: SizedBox(
|
||||
height: 44,
|
||||
width: 44,
|
||||
child: ButtonTheme(
|
||||
minWidth: double.minPositive,
|
||||
child: FlatButton(
|
||||
highlightColor: Colors.transparent,
|
||||
splashColor: Colors.transparent,
|
||||
padding: EdgeInsets.all(0),
|
||||
onPressed: () async {
|
||||
await showDialog<void>(
|
||||
builder: (_) => MenuWidget(), context: context);
|
||||
},
|
||||
child: menuButton),
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(left: 20, top: 20),
|
||||
child: WalletCard(walletVM: widget.walletViewModel)),
|
||||
SizedBox(height: 28),
|
||||
Expanded(child: TradeHistoryPanel(dashboardViewModel: widget.walletViewModel))
|
||||
],
|
||||
),
|
||||
),
|
||||
], begin: Alignment.topLeft, end: Alignment.bottomRight)),
|
||||
child: scaffold);
|
||||
|
||||
@override
|
||||
Widget trailing(BuildContext context) {
|
||||
final menuButton = Image.asset('assets/images/header.png',
|
||||
color: Theme.of(context).primaryTextTheme.title.color);
|
||||
|
||||
return Container(
|
||||
alignment: Alignment.centerRight,
|
||||
child: SizedBox(
|
||||
width: 24,
|
||||
child: FlatButton(
|
||||
highlightColor: Colors.transparent,
|
||||
splashColor: Colors.transparent,
|
||||
padding: EdgeInsets.all(0),
|
||||
onPressed: () async {
|
||||
await showDialog<void>(
|
||||
builder: (_) => MenuWidget(
|
||||
name: walletViewModel.name,
|
||||
subname: walletViewModel.subname,
|
||||
type: walletViewModel.type),
|
||||
context: context);
|
||||
},
|
||||
child: menuButton),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
final DashboardViewModel walletViewModel;
|
||||
final sendImage = Image.asset('assets/images/send.png');
|
||||
final exchangeImage = Image.asset('assets/images/exchange.png');
|
||||
final buyImage = Image.asset('assets/images/coins.png');
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) {
|
||||
return LayoutBuilder(builder: (context, constraints) {
|
||||
final transactionListMinHeight =
|
||||
constraints.heightConstraints().maxHeight - 345 - 32;
|
||||
|
||||
return SingleChildScrollView(
|
||||
child: Column(children: [
|
||||
Container(
|
||||
height: 345,
|
||||
child: Column(children: [
|
||||
Padding(
|
||||
padding: EdgeInsets.only(left: 24, top: 10),
|
||||
child: WalletCard(walletVM: walletViewModel)),
|
||||
Container(
|
||||
padding: EdgeInsets.only(left: 44, right: 44, top: 32),
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Flexible(
|
||||
child: actionButton(
|
||||
context: context,
|
||||
image: sendImage,
|
||||
title: S.of(context).send,
|
||||
route: Routes.send)),
|
||||
Flexible(
|
||||
child: actionButton(
|
||||
context: context,
|
||||
image: exchangeImage,
|
||||
title: S.of(context).exchange,
|
||||
route: Routes.exchange)),
|
||||
],
|
||||
),
|
||||
)
|
||||
])),
|
||||
SizedBox(height: 32),
|
||||
ConstrainedBox(
|
||||
constraints: BoxConstraints(minHeight: transactionListMinHeight),
|
||||
child: TradeHistoryPanel(dashboardViewModel: walletViewModel)),
|
||||
// Column(children: [
|
||||
// Text('1'),
|
||||
// Text('2')
|
||||
// ])
|
||||
]));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Widget actionButton(
|
||||
{BuildContext context,
|
||||
@required Image image,
|
||||
@required String title,
|
||||
@required String route}) {
|
||||
return Container(
|
||||
width: MediaQuery.of(context).size.width,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
if (route.isNotEmpty) {
|
||||
Navigator.of(context, rootNavigator: true).pushNamed(route);
|
||||
}
|
||||
},
|
||||
child: Container(
|
||||
height: 48,
|
||||
width: 48,
|
||||
alignment: Alignment.center,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).primaryTextTheme.subhead.color,
|
||||
shape: BoxShape.circle),
|
||||
child: image,
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 12),
|
||||
child: Text(
|
||||
title,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Color.fromRGBO(140, 153, 201,
|
||||
0.8) // Theme.of(context).primaryTextTheme.caption.color
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -16,10 +16,8 @@ class ButtonHeader extends SliverPersistentHeaderDelegate {
|
||||
final buyImage = Image.asset('assets/images/coins.png');
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) {
|
||||
// final actionListStore = Provider.of<ActionListStore>(context);
|
||||
final historyPanelWidth = MediaQuery.of(context).size.width;
|
||||
|
||||
Widget build(
|
||||
BuildContext context, double shrinkOffset, bool overlapsContent) {
|
||||
final _themeChanger = Provider.of<ThemeChanger>(context);
|
||||
Image filterButton;
|
||||
|
||||
@@ -29,74 +27,39 @@ class ButtonHeader extends SliverPersistentHeaderDelegate {
|
||||
filterButton = Image.asset('assets/images/filter_light_button.png');
|
||||
}
|
||||
|
||||
double buttonsOpacity = 1 - shrinkOffset / (maxExtent - minExtent);
|
||||
double buttonsHeight = maxExtent - minExtent - shrinkOffset;
|
||||
|
||||
buttonsOpacity = buttonsOpacity >= 0 ? buttonsOpacity : 0;
|
||||
buttonsHeight = buttonsHeight >= 0 ? buttonsHeight : 0;
|
||||
|
||||
return Stack(
|
||||
fit: StackFit.expand,
|
||||
overflow: Overflow.visible,
|
||||
children: <Widget>[
|
||||
Opacity(
|
||||
opacity: buttonsOpacity,
|
||||
child: Container(
|
||||
height: buttonsHeight,
|
||||
padding: EdgeInsets.only(left: 44, right: 44),
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Flexible(
|
||||
child: actionButton(
|
||||
context: context,
|
||||
image: sendImage,
|
||||
title: S.of(context).send,
|
||||
route: Routes.send
|
||||
)
|
||||
),
|
||||
Flexible(
|
||||
child: actionButton(
|
||||
context: context,
|
||||
image: exchangeImage,
|
||||
title: S.of(context).exchange,
|
||||
route: Routes.exchange
|
||||
)
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
top: buttonsHeight,
|
||||
left: 0,
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.only(topLeft: Radius.circular(20), topRight: Radius.circular(20)),
|
||||
child: Container(
|
||||
width: historyPanelWidth,
|
||||
height: 66,
|
||||
padding: EdgeInsets.only(top: 20, left: 20, right: 20, bottom: 10),
|
||||
color: Theme.of(context).backgroundColor,
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
S.of(context).transactions,
|
||||
style: TextStyle(
|
||||
fontSize: 20,
|
||||
color: Theme.of(context).primaryTextTheme.title.color
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
right: 0,
|
||||
child: PopupMenuButton<int>(
|
||||
itemBuilder: (context) => [
|
||||
PopupMenuItem(
|
||||
enabled: false,
|
||||
value: -1,
|
||||
child: Text(S.of(context).transactions,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Theme.of(context).primaryTextTheme.caption.color))),
|
||||
return ClipRRect(
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(24), topRight: Radius.circular(24)),
|
||||
child: Container(
|
||||
color: Colors.red,
|
||||
// height: 75,
|
||||
padding: EdgeInsets.only(top: 26, left: 20, right: 20, bottom: 10),
|
||||
// color: Theme.of(context).backgroundColor,
|
||||
child: Stack(
|
||||
children: <Widget>[
|
||||
Center(
|
||||
child: Text(
|
||||
S.of(context).transactions,
|
||||
style: TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Theme.of(context).primaryTextTheme.title.color),
|
||||
)),
|
||||
Positioned(
|
||||
right: 0,
|
||||
height: 36,
|
||||
child: PopupMenuButton<int>(
|
||||
itemBuilder: (context) => [
|
||||
PopupMenuItem(
|
||||
enabled: false,
|
||||
value: -1,
|
||||
child: Text(S.of(context).transactions,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.caption
|
||||
.color))),
|
||||
// PopupMenuItem(
|
||||
// value: 0,
|
||||
// child: Observer(
|
||||
@@ -135,27 +98,28 @@ class ButtonHeader extends SliverPersistentHeaderDelegate {
|
||||
// .toggleOutgoing(),
|
||||
// )
|
||||
// ]))),
|
||||
PopupMenuItem(
|
||||
value: 2,
|
||||
child:
|
||||
Text(S.of(context).transactions_by_date)),
|
||||
PopupMenuDivider(),
|
||||
PopupMenuItem(
|
||||
enabled: false,
|
||||
value: -1,
|
||||
child: Text(S.of(context).trades,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Theme.of(context).primaryTextTheme.caption.color))),
|
||||
PopupMenuItem(
|
||||
value: 3,
|
||||
child: Observer(
|
||||
builder: (_) => Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment
|
||||
.spaceBetween,
|
||||
children: [
|
||||
Text('XMR.TO'),
|
||||
PopupMenuItem(
|
||||
value: 2,
|
||||
child: Text(S.of(context).transactions_by_date)),
|
||||
PopupMenuDivider(),
|
||||
PopupMenuItem(
|
||||
enabled: false,
|
||||
value: -1,
|
||||
child: Text(S.of(context).trades,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.caption
|
||||
.color))),
|
||||
PopupMenuItem(
|
||||
value: 3,
|
||||
child: Observer(
|
||||
builder: (_) => Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text('XMR.TO'),
|
||||
// Checkbox(
|
||||
// value: actionListStore
|
||||
// .tradeFilterStore
|
||||
@@ -167,16 +131,15 @@ class ButtonHeader extends SliverPersistentHeaderDelegate {
|
||||
// ExchangeProviderDescription
|
||||
// .xmrto),
|
||||
// )
|
||||
]))),
|
||||
PopupMenuItem(
|
||||
value: 4,
|
||||
child: Observer(
|
||||
builder: (_) => Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment
|
||||
.spaceBetween,
|
||||
children: [
|
||||
Text('Change.NOW'),
|
||||
]))),
|
||||
PopupMenuItem(
|
||||
value: 4,
|
||||
child: Observer(
|
||||
builder: (_) => Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text('Change.NOW'),
|
||||
// Checkbox(
|
||||
// value: actionListStore
|
||||
// .tradeFilterStore
|
||||
@@ -188,16 +151,15 @@ class ButtonHeader extends SliverPersistentHeaderDelegate {
|
||||
// ExchangeProviderDescription
|
||||
// .changeNow),
|
||||
// )
|
||||
]))),
|
||||
PopupMenuItem(
|
||||
value: 5,
|
||||
child: Observer(
|
||||
builder: (_) => Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment
|
||||
.spaceBetween,
|
||||
children: [
|
||||
Text('MorphToken'),
|
||||
]))),
|
||||
PopupMenuItem(
|
||||
value: 5,
|
||||
child: Observer(
|
||||
builder: (_) => Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text('MorphToken'),
|
||||
// Checkbox(
|
||||
// value: actionListStore
|
||||
// .tradeFilterStore
|
||||
@@ -209,42 +171,37 @@ class ButtonHeader extends SliverPersistentHeaderDelegate {
|
||||
// ExchangeProviderDescription
|
||||
// .morphToken),
|
||||
// )
|
||||
])))
|
||||
],
|
||||
child: filterButton,
|
||||
onSelected: (item) async {
|
||||
if (item == 2) {
|
||||
final List<DateTime> picked =
|
||||
await date_rage_picker.showDatePicker(
|
||||
context: context,
|
||||
initialFirstDate: DateTime.now()
|
||||
.subtract(Duration(days: 1)),
|
||||
initialLastDate: (DateTime.now()),
|
||||
firstDate: DateTime(2015),
|
||||
lastDate: DateTime.now()
|
||||
.add(Duration(days: 1)));
|
||||
])))
|
||||
],
|
||||
child: filterButton,
|
||||
onSelected: (item) async {
|
||||
if (item == 2) {
|
||||
final List<DateTime> picked =
|
||||
await date_rage_picker.showDatePicker(
|
||||
context: context,
|
||||
initialFirstDate:
|
||||
DateTime.now().subtract(Duration(days: 1)),
|
||||
initialLastDate: (DateTime.now()),
|
||||
firstDate: DateTime(2015),
|
||||
lastDate: DateTime.now().add(Duration(days: 1)));
|
||||
|
||||
if (picked != null && picked.length == 2) {
|
||||
if (picked != null && picked.length == 2) {
|
||||
// actionListStore.transactionFilterStore
|
||||
// .changeStartDate(picked.first);
|
||||
// actionListStore.transactionFilterStore
|
||||
// .changeEndDate(picked.last);
|
||||
}
|
||||
}
|
||||
},
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
)
|
||||
],
|
||||
}
|
||||
}
|
||||
},
|
||||
)),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
double get maxExtent => 174;
|
||||
double get maxExtent => 164;
|
||||
|
||||
@override
|
||||
double get minExtent => 66;
|
||||
@@ -252,12 +209,11 @@ class ButtonHeader extends SliverPersistentHeaderDelegate {
|
||||
@override
|
||||
bool shouldRebuild(SliverPersistentHeaderDelegate oldDelegate) => true;
|
||||
|
||||
Widget actionButton({
|
||||
BuildContext context,
|
||||
@required Image image,
|
||||
@required String title,
|
||||
@required String route}) {
|
||||
|
||||
Widget actionButton(
|
||||
{BuildContext context,
|
||||
@required Image image,
|
||||
@required String title,
|
||||
@required String route}) {
|
||||
return Container(
|
||||
width: MediaQuery.of(context).size.width,
|
||||
child: Column(
|
||||
@@ -276,23 +232,24 @@ class ButtonHeader extends SliverPersistentHeaderDelegate {
|
||||
alignment: Alignment.center,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).primaryTextTheme.subhead.color,
|
||||
shape: BoxShape.circle
|
||||
),
|
||||
shape: BoxShape.circle),
|
||||
child: image,
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 10),
|
||||
padding: EdgeInsets.only(top: 12),
|
||||
child: Text(
|
||||
title,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
color: Theme.of(context).primaryTextTheme.caption.color
|
||||
),
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Color.fromRGBO(140, 153, 201,
|
||||
0.8) // Theme.of(context).primaryTextTheme.caption.color
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,25 +1,28 @@
|
||||
import 'dart:async';
|
||||
import 'dart:ui';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:cake_wallet/palette.dart';
|
||||
import 'package:cake_wallet/src/domain/common/wallet_type.dart';
|
||||
import 'package:cake_wallet/src/screens/dashboard/wallet_menu.dart';
|
||||
import 'package:cake_wallet/src/stores/wallet/wallet_store.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class MenuWidget extends StatefulWidget {
|
||||
MenuWidget({this.type, this.name, this.subname});
|
||||
|
||||
final WalletType type;
|
||||
final String name;
|
||||
final String subname;
|
||||
|
||||
@override
|
||||
MenuWidgetState createState() => MenuWidgetState();
|
||||
}
|
||||
|
||||
class MenuWidgetState extends State<MenuWidget> {
|
||||
final moneroIcon = Image.asset('assets/images/monero.png');
|
||||
final bitcoinIcon = Image.asset('assets/images/bitcoin.png');
|
||||
final largeScreen = 731;
|
||||
|
||||
double menuWidth;
|
||||
double screenWidth;
|
||||
double screenHeight;
|
||||
double opacity;
|
||||
bool isDraw;
|
||||
|
||||
double headerHeight;
|
||||
double tileHeight;
|
||||
@@ -32,7 +35,6 @@ class MenuWidgetState extends State<MenuWidget> {
|
||||
screenWidth = 0;
|
||||
screenHeight = 0;
|
||||
opacity = 0;
|
||||
isDraw = false;
|
||||
|
||||
headerHeight = 120;
|
||||
tileHeight = 75;
|
||||
@@ -59,206 +61,205 @@ class MenuWidgetState extends State<MenuWidget> {
|
||||
fromBottomEdge *= scale;
|
||||
}
|
||||
});
|
||||
|
||||
Timer(Duration(milliseconds: 350), () =>
|
||||
setState(() => isDraw = true)
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final walletMenu = WalletMenu(context);
|
||||
final walletStore = Provider.of<WalletStore>(context);
|
||||
// final walletStore = Provider.of<WalletStore>(context);
|
||||
final itemCount = walletMenu.items.length;
|
||||
|
||||
return GestureDetector(
|
||||
onTap: () => Navigator.of(context).pop(),
|
||||
child: Container(
|
||||
color: Colors.transparent,
|
||||
child: BackdropFilter(
|
||||
filter: ImageFilter.blur(sigmaX: 3.0, sigmaY: 3.0),
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: EdgeInsets.only(left: 24),
|
||||
child: Container(
|
||||
height: 60,
|
||||
width: 4,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.all(Radius.circular(2)),
|
||||
color: Theme.of(context).hintColor),
|
||||
)),
|
||||
SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: GestureDetector(
|
||||
onTap: () => null,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(color: PaletteDark.darkNightBlue.withOpacity(0.75)),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: EdgeInsets.only(left: 24),
|
||||
child: isDraw
|
||||
? Container(
|
||||
height: 60,
|
||||
width: 4,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.all(Radius.circular(2)),
|
||||
color: Theme.of(context).hintColor //
|
||||
),
|
||||
)
|
||||
: Container(
|
||||
height: 60,
|
||||
width: 4,
|
||||
)
|
||||
),
|
||||
SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: GestureDetector(
|
||||
onTap: () => null,
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
height: double.infinity,
|
||||
alignment: Alignment.centerRight,
|
||||
child: AnimatedContainer(
|
||||
alignment: Alignment.centerLeft,
|
||||
width: menuWidth,
|
||||
height: double.infinity,
|
||||
duration: Duration(milliseconds: 500),
|
||||
curve: Curves.fastOutSlowIn,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.only(topLeft: Radius.circular(24), bottomLeft: Radius.circular(24)),
|
||||
color: Theme.of(context).primaryTextTheme.display1.color.withOpacity(opacity)
|
||||
),
|
||||
child: isDraw
|
||||
? ListView.separated(
|
||||
itemBuilder: (_, index) {
|
||||
|
||||
if (index == 0) {
|
||||
return Container(
|
||||
height: headerHeight,
|
||||
padding: EdgeInsets.only(
|
||||
left: 24,
|
||||
top: fromTopEdge,
|
||||
right: 24,
|
||||
bottom: fromBottomEdge),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.only(topLeft: Radius.circular(24)),
|
||||
color: Theme.of(context).primaryTextTheme.display2.color
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
moneroIcon,
|
||||
SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: Container(
|
||||
height: 40,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
walletStore.name,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).primaryTextTheme.title.color,
|
||||
decoration: TextDecoration.none,
|
||||
fontFamily: 'Lato',
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold
|
||||
),
|
||||
),
|
||||
Text(
|
||||
walletStore.account.label,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).primaryTextTheme.caption.color,
|
||||
decoration: TextDecoration.none,
|
||||
fontFamily: 'Lato',
|
||||
fontSize: 12
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
)
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
index -= 1;
|
||||
final item = walletMenu.items[index];
|
||||
final image = walletMenu.images[index] ?? Offstage();
|
||||
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
Navigator.of(context).pop();
|
||||
walletMenu.action(index);
|
||||
},
|
||||
child: index == itemCount - 1
|
||||
? Container(
|
||||
height: headerHeight,
|
||||
padding: EdgeInsets.only(
|
||||
left: 24,
|
||||
right: 24,
|
||||
top: fromBottomEdge,
|
||||
bottom: fromTopEdge),
|
||||
alignment: Alignment.topLeft,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.only(bottomLeft: Radius.circular(24)),
|
||||
color: Theme.of(context).primaryTextTheme.display1.color,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
image,
|
||||
SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: Text(
|
||||
item,
|
||||
style: TextStyle(
|
||||
decoration: TextDecoration.none,
|
||||
color: Theme.of(context).primaryTextTheme.title.color,
|
||||
fontFamily: 'Lato',
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold
|
||||
),
|
||||
)
|
||||
)
|
||||
],
|
||||
),
|
||||
)
|
||||
: Container(
|
||||
height: tileHeight,
|
||||
padding: EdgeInsets.only(left: 24, right: 24),
|
||||
color: Theme.of(context).primaryTextTheme.display1.color,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
image,
|
||||
SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: Text(
|
||||
item,
|
||||
style: TextStyle(
|
||||
decoration: TextDecoration.none,
|
||||
color: Theme.of(context).primaryTextTheme.title.color,
|
||||
fontFamily: 'Lato',
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold
|
||||
),
|
||||
)
|
||||
)
|
||||
],
|
||||
),
|
||||
width: menuWidth,
|
||||
height: double.infinity,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(24),
|
||||
bottomLeft: Radius.circular(24)),
|
||||
color: Theme.of(context).primaryTextTheme.display1.color),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(24),
|
||||
bottomLeft: Radius.circular(24)),
|
||||
child: ListView.separated(
|
||||
itemBuilder: (_, index) {
|
||||
if (index == 0) {
|
||||
return Container(
|
||||
height: headerHeight,
|
||||
padding: EdgeInsets.only(
|
||||
left: 24,
|
||||
top: fromTopEdge,
|
||||
right: 24,
|
||||
bottom: fromBottomEdge),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius:
|
||||
BorderRadius.only(topLeft: Radius.circular(24)),
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.display2
|
||||
.color),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
_iconFor(type: widget.type),
|
||||
SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: Container(
|
||||
height: 40,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: widget.subname != null
|
||||
? MainAxisAlignment.spaceBetween
|
||||
: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
widget.name,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.title
|
||||
.color,
|
||||
decoration: TextDecoration.none,
|
||||
fontFamily: 'Avenir Next',
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold),
|
||||
),
|
||||
);
|
||||
},
|
||||
separatorBuilder: (_, index) =>
|
||||
Container(
|
||||
height: 1,
|
||||
color: Theme.of(context).dividerColor,
|
||||
),
|
||||
itemCount: itemCount + 1)
|
||||
: Offstage()
|
||||
if (widget.subname != null)
|
||||
Text(
|
||||
widget.subname,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.caption
|
||||
.color,
|
||||
decoration: TextDecoration.none,
|
||||
fontFamily: 'Avenir Next',
|
||||
fontSize: 12),
|
||||
)
|
||||
],
|
||||
),
|
||||
))
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
index -= 1;
|
||||
final item = walletMenu.items[index];
|
||||
final image = walletMenu.images[index] ?? Offstage();
|
||||
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
Navigator.of(context).pop();
|
||||
walletMenu.action(index);
|
||||
},
|
||||
child: index == itemCount - 1
|
||||
? Container(
|
||||
height: headerHeight,
|
||||
padding: EdgeInsets.only(
|
||||
left: 24,
|
||||
right: 24,
|
||||
top: fromBottomEdge,
|
||||
bottom: fromTopEdge),
|
||||
alignment: Alignment.topLeft,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.only(
|
||||
bottomLeft: Radius.circular(24)),
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.display1
|
||||
.color,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
image,
|
||||
SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: Text(
|
||||
item,
|
||||
style: TextStyle(
|
||||
decoration: TextDecoration.none,
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.title
|
||||
.color,
|
||||
fontFamily: 'Avenir Next',
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold),
|
||||
))
|
||||
],
|
||||
),
|
||||
)
|
||||
: Container(
|
||||
height: tileHeight,
|
||||
padding: EdgeInsets.only(left: 24, right: 24),
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.display1
|
||||
.color,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
image,
|
||||
SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: Text(
|
||||
item,
|
||||
style: TextStyle(
|
||||
decoration: TextDecoration.none,
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.title
|
||||
.color,
|
||||
fontFamily: 'Avenir Next',
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold),
|
||||
))
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
separatorBuilder: (_, index) => Container(
|
||||
height: 1,
|
||||
color: Theme.of(context).dividerColor,
|
||||
),
|
||||
),
|
||||
)
|
||||
)
|
||||
],
|
||||
)
|
||||
itemCount: itemCount + 1),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
))
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Image _iconFor({@required WalletType type}) {
|
||||
switch (type) {
|
||||
case WalletType.monero:
|
||||
return moneroIcon;
|
||||
case WalletType.bitcoin:
|
||||
return bitcoinIcon;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/theme_changer.dart';
|
||||
import 'package:cake_wallet/themes.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
@@ -15,11 +18,12 @@ import 'date_section_raw.dart';
|
||||
import 'trade_row.dart';
|
||||
import 'transaction_raw.dart';
|
||||
import 'button_header.dart';
|
||||
import 'package:date_range_picker/date_range_picker.dart' as date_rage_picker;
|
||||
|
||||
class TradeHistoryPanel extends StatefulWidget {
|
||||
TradeHistoryPanel({this.dashboardViewModel});
|
||||
|
||||
DashboardViewModel dashboardViewModel;
|
||||
final DashboardViewModel dashboardViewModel;
|
||||
|
||||
@override
|
||||
TradeHistoryPanelState createState() => TradeHistoryPanelState();
|
||||
@@ -49,127 +53,270 @@ class TradeHistoryPanelState extends State<TradeHistoryPanel> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// final actionListStore = Provider.of<ActionListStore>(context);
|
||||
// final settingsStore = Provider.of<SettingsStore>(context);
|
||||
// AnimatedContainer(
|
||||
// width: MediaQuery.of(context).size.width,
|
||||
// height: panelHeight,
|
||||
// duration: Duration(milliseconds: 1000),
|
||||
// curve: Curves.fastOutSlowIn,
|
||||
// child: )
|
||||
|
||||
final transactionDateFormat = DateFormat('HH:mm');
|
||||
final _themeChanger = Provider.of<ThemeChanger>(context);
|
||||
final filterButton = Image.asset(
|
||||
_themeChanger.getTheme() == Themes.darkTheme
|
||||
? 'assets/images/filter_button.png'
|
||||
: 'assets/images/filter_light_button.png',
|
||||
height: 36);
|
||||
|
||||
return Container(
|
||||
height: MediaQuery.of(context).size.height,
|
||||
width: MediaQuery.of(context).size.width,
|
||||
alignment: Alignment.bottomCenter,
|
||||
child: AnimatedContainer(
|
||||
width: MediaQuery.of(context).size.width,
|
||||
height: panelHeight,
|
||||
duration: Duration(milliseconds: 1000),
|
||||
curve: Curves.fastOutSlowIn,
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(20),
|
||||
topRight: Radius.circular(20)),
|
||||
child: CustomScrollView(
|
||||
slivers: <Widget>[
|
||||
SliverPersistentHeader(
|
||||
delegate: ButtonHeader(),
|
||||
pinned: true,
|
||||
floating: false,
|
||||
),
|
||||
Observer(
|
||||
key: _listObserverKey,
|
||||
builder: (_) {
|
||||
// final items = actionListStore.items == null
|
||||
// ? <String>[]
|
||||
// : actionListStore.items;
|
||||
final items = widget.dashboardViewModel.transactions;
|
||||
final itemsCount = items.length + 1;
|
||||
final symbol =
|
||||
'\$'; // settingsStore.fiatCurrency.toString();
|
||||
var freeSpaceHeight =
|
||||
MediaQuery.of(context).size.height - 496;
|
||||
return ClipRRect(
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(20), topRight: Radius.circular(20)),
|
||||
child: Container(
|
||||
color: Colors.white,
|
||||
child: Column(children: [
|
||||
Container(
|
||||
padding:
|
||||
EdgeInsets.only(top: 32, left: 20, right: 20, bottom: 20),
|
||||
color: Theme.of(context).backgroundColor,
|
||||
child: Stack(
|
||||
children: <Widget>[
|
||||
SizedBox(height: 37), // Force stack height
|
||||
Center(
|
||||
child: Text(S.of(context).transactions,
|
||||
style: TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.title
|
||||
.color))),
|
||||
Positioned(
|
||||
right: 0,
|
||||
child: PopupMenuButton<int>(
|
||||
itemBuilder: (context) => [
|
||||
PopupMenuItem(
|
||||
enabled: false,
|
||||
value: -1,
|
||||
child: Text(S.of(context).transactions,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.caption
|
||||
.color))),
|
||||
// PopupMenuItem(
|
||||
// value: 0,
|
||||
// child: Observer(
|
||||
// builder: (_) => Row(
|
||||
// mainAxisAlignment:
|
||||
// MainAxisAlignment
|
||||
// .spaceBetween,
|
||||
// children: [
|
||||
// Text(S.of(context).incoming),
|
||||
// Checkbox(
|
||||
// value: actionListStore
|
||||
// .transactionFilterStore
|
||||
// .displayIncoming,
|
||||
// onChanged: (value) =>
|
||||
// actionListStore
|
||||
// .transactionFilterStore
|
||||
// .toggleIncoming(),
|
||||
// )
|
||||
// ]))),
|
||||
// PopupMenuItem(
|
||||
// value: 1,
|
||||
// child: Observer(
|
||||
// builder: (_) => Row(
|
||||
// mainAxisAlignment:
|
||||
// MainAxisAlignment
|
||||
// .spaceBetween,
|
||||
// children: [
|
||||
// Text(S.of(context).outgoing),
|
||||
// Checkbox(
|
||||
// value: actionListStore
|
||||
// .transactionFilterStore
|
||||
// .displayOutgoing,
|
||||
// onChanged: (value) =>
|
||||
// actionListStore
|
||||
// .transactionFilterStore
|
||||
// .toggleOutgoing(),
|
||||
// )
|
||||
// ]))),
|
||||
PopupMenuItem(
|
||||
value: 2,
|
||||
child:
|
||||
Text(S.of(context).transactions_by_date)),
|
||||
PopupMenuDivider(),
|
||||
PopupMenuItem(
|
||||
enabled: false,
|
||||
value: -1,
|
||||
child: Text(S.of(context).trades,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.caption
|
||||
.color))),
|
||||
PopupMenuItem(
|
||||
value: 3,
|
||||
child: Observer(
|
||||
builder: (_) => Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text('XMR.TO'),
|
||||
// Checkbox(
|
||||
// value: actionListStore
|
||||
// .tradeFilterStore
|
||||
// .displayXMRTO,
|
||||
// onChanged: (value) =>
|
||||
// actionListStore
|
||||
// .tradeFilterStore
|
||||
// .toggleDisplayExchange(
|
||||
// ExchangeProviderDescription
|
||||
// .xmrto),
|
||||
// )
|
||||
]))),
|
||||
PopupMenuItem(
|
||||
value: 4,
|
||||
child: Observer(
|
||||
builder: (_) => Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text('Change.NOW'),
|
||||
// Checkbox(
|
||||
// value: actionListStore
|
||||
// .tradeFilterStore
|
||||
// .displayChangeNow,
|
||||
// onChanged: (value) =>
|
||||
// actionListStore
|
||||
// .tradeFilterStore
|
||||
// .toggleDisplayExchange(
|
||||
// ExchangeProviderDescription
|
||||
// .changeNow),
|
||||
// )
|
||||
]))),
|
||||
PopupMenuItem(
|
||||
value: 5,
|
||||
child: Observer(
|
||||
builder: (_) => Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text('MorphToken'),
|
||||
// Checkbox(
|
||||
// value: actionListStore
|
||||
// .tradeFilterStore
|
||||
// .displayMorphToken,
|
||||
// onChanged: (value) =>
|
||||
// actionListStore
|
||||
// .tradeFilterStore
|
||||
// .toggleDisplayExchange(
|
||||
// ExchangeProviderDescription
|
||||
// .morphToken),
|
||||
// )
|
||||
])))
|
||||
],
|
||||
child: filterButton,
|
||||
onSelected: (item) async {
|
||||
if (item == 2) {
|
||||
final picked =
|
||||
await date_rage_picker.showDatePicker(
|
||||
context: context,
|
||||
initialFirstDate: DateTime.now()
|
||||
.subtract(Duration(days: 1)),
|
||||
initialLastDate: (DateTime.now()),
|
||||
firstDate: DateTime(2015),
|
||||
lastDate: DateTime.now()
|
||||
.add(Duration(days: 1)));
|
||||
|
||||
return SliverList(
|
||||
key: _listKey,
|
||||
delegate:
|
||||
SliverChildBuilderDelegate((context, index) {
|
||||
if (index == itemsCount - 1) {
|
||||
freeSpaceHeight = freeSpaceHeight >= 0
|
||||
? freeSpaceHeight
|
||||
: 0;
|
||||
|
||||
return Container(
|
||||
height: freeSpaceHeight,
|
||||
width: MediaQuery.of(context).size.width,
|
||||
color: Theme.of(context).backgroundColor);
|
||||
}
|
||||
|
||||
final item = items[index];
|
||||
|
||||
if (item is DateSectionItem) {
|
||||
freeSpaceHeight -= 38;
|
||||
return DateSectionRaw(date: item.date);
|
||||
}
|
||||
|
||||
if (item is TransactionListItem) {
|
||||
freeSpaceHeight -= 62;
|
||||
final transaction = item.transaction;
|
||||
final savedDisplayMode =
|
||||
BalanceDisplayMode.all;
|
||||
//settingsStore
|
||||
// .balanceDisplayMode;
|
||||
final formattedAmount = savedDisplayMode ==
|
||||
BalanceDisplayMode.hiddenBalance
|
||||
? '---'
|
||||
: transaction.amountFormatted();
|
||||
final formattedFiatAmount =
|
||||
savedDisplayMode ==
|
||||
BalanceDisplayMode.hiddenBalance
|
||||
? '---'
|
||||
: transaction
|
||||
.fiatAmount(); // symbol ???
|
||||
|
||||
return TransactionRow(
|
||||
onTap: () => Navigator.of(context)
|
||||
.pushNamed(Routes.transactionDetails,
|
||||
arguments: transaction),
|
||||
direction: transaction.direction,
|
||||
formattedDate: transactionDateFormat
|
||||
.format(transaction.date),
|
||||
formattedAmount: formattedAmount,
|
||||
formattedFiatAmount: formattedFiatAmount,
|
||||
isPending: transaction.isPending);
|
||||
}
|
||||
|
||||
if (item is TradeListItem) {
|
||||
freeSpaceHeight -= 62;
|
||||
final trade = item.trade;
|
||||
final savedDisplayMode =
|
||||
BalanceDisplayMode.all;
|
||||
//settingsStore
|
||||
// .balanceDisplayMode;
|
||||
final formattedAmount = trade.amount != null
|
||||
? savedDisplayMode ==
|
||||
BalanceDisplayMode.hiddenBalance
|
||||
? '---'
|
||||
: trade.amountFormatted()
|
||||
: trade.amount;
|
||||
|
||||
return TradeRow(
|
||||
onTap: () => Navigator.of(context)
|
||||
.pushNamed(Routes.tradeDetails,
|
||||
arguments: trade),
|
||||
provider: trade.provider,
|
||||
from: trade.from,
|
||||
to: trade.to,
|
||||
createdAtFormattedDate:
|
||||
transactionDateFormat
|
||||
.format(trade.createdAt),
|
||||
formattedAmount: formattedAmount);
|
||||
}
|
||||
|
||||
return Container(
|
||||
color: Theme.of(context).backgroundColor);
|
||||
}, childCount: itemsCount));
|
||||
})
|
||||
if (picked != null && picked.length == 2) {
|
||||
// actionListStore.transactionFilterStore
|
||||
// .changeStartDate(picked.first);
|
||||
// actionListStore.transactionFilterStore
|
||||
// .changeEndDate(picked.last);
|
||||
}
|
||||
}
|
||||
},
|
||||
)),
|
||||
],
|
||||
)))); //,
|
||||
),
|
||||
),
|
||||
widget.dashboardViewModel.transactions?.isNotEmpty ?? false
|
||||
? ListView.separated(
|
||||
physics: NeverScrollableScrollPhysics(),
|
||||
shrinkWrap: true,
|
||||
itemCount: widget.dashboardViewModel.transactions.length,
|
||||
itemBuilder: (_, index) {
|
||||
final item =
|
||||
widget.dashboardViewModel.transactions[index];
|
||||
|
||||
if (item is DateSectionItem) {
|
||||
return DateSectionRaw(date: item.date);
|
||||
}
|
||||
|
||||
if (item is TransactionListItem) {
|
||||
final transaction = item.transaction;
|
||||
final savedDisplayMode = BalanceDisplayMode.all;
|
||||
//settingsStore
|
||||
// .balanceDisplayMode;
|
||||
final formattedAmount = savedDisplayMode ==
|
||||
BalanceDisplayMode.hiddenBalance
|
||||
? '---'
|
||||
: transaction.amountFormatted();
|
||||
final formattedFiatAmount = savedDisplayMode ==
|
||||
BalanceDisplayMode.hiddenBalance
|
||||
? '---'
|
||||
: transaction.fiatAmount(); // symbol ???
|
||||
|
||||
return TransactionRow(
|
||||
onTap: () => Navigator.of(context).pushNamed(
|
||||
Routes.transactionDetails,
|
||||
arguments: transaction),
|
||||
direction: transaction.direction,
|
||||
formattedDate: transactionDateFormat
|
||||
.format(transaction.date),
|
||||
formattedAmount: formattedAmount,
|
||||
formattedFiatAmount: formattedFiatAmount,
|
||||
isPending: transaction.isPending);
|
||||
}
|
||||
|
||||
if (item is TradeListItem) {
|
||||
final trade = item.trade;
|
||||
final savedDisplayMode = BalanceDisplayMode.all;
|
||||
//settingsStore
|
||||
// .balanceDisplayMode;
|
||||
final formattedAmount = trade.amount != null
|
||||
? savedDisplayMode ==
|
||||
BalanceDisplayMode.hiddenBalance
|
||||
? '---'
|
||||
: trade.amountFormatted()
|
||||
: trade.amount;
|
||||
|
||||
return TradeRow(
|
||||
onTap: () => Navigator.of(context).pushNamed(
|
||||
Routes.tradeDetails,
|
||||
arguments: trade),
|
||||
provider: trade.provider,
|
||||
from: trade.from,
|
||||
to: trade.to,
|
||||
createdAtFormattedDate:
|
||||
transactionDateFormat.format(trade.createdAt),
|
||||
formattedAmount: formattedAmount);
|
||||
}
|
||||
|
||||
return Container(
|
||||
color: Theme.of(context).backgroundColor,
|
||||
height: 1);
|
||||
},
|
||||
separatorBuilder: (_, __) =>
|
||||
Container(height: 14, color: Colors.white),
|
||||
)
|
||||
: Padding(
|
||||
padding: EdgeInsets.all(20),
|
||||
child: Text('Your transactions will be displayed here!',
|
||||
style: TextStyle(color: Colors.grey)))
|
||||
]))); //,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,27 +24,19 @@ class TransactionRow extends StatelessWidget {
|
||||
return InkWell(
|
||||
onTap: onTap,
|
||||
child: Container(
|
||||
height: 60,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).backgroundColor,
|
||||
border: Border.all(
|
||||
width: 1,
|
||||
color: Theme.of(context).backgroundColor
|
||||
),
|
||||
),
|
||||
padding: EdgeInsets.only(top: 5, bottom: 5, left: 20, right: 20),
|
||||
height: 41,
|
||||
color: Theme.of(context).backgroundColor,
|
||||
padding: EdgeInsets.only(left: 20, right: 20),
|
||||
child: Row(children: <Widget>[
|
||||
Container(
|
||||
height: 36,
|
||||
width: 36,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: Theme.of(context).primaryTextTheme.display3.color
|
||||
),
|
||||
child: Image.asset(
|
||||
direction == TransactionDirection.incoming
|
||||
? 'assets/images/down_arrow.png'
|
||||
: 'assets/images/up_arrow.png'),
|
||||
shape: BoxShape.circle,
|
||||
color: Theme.of(context).primaryTextTheme.display3.color),
|
||||
child: Image.asset(direction == TransactionDirection.incoming
|
||||
? 'assets/images/down_arrow.png'
|
||||
: 'assets/images/up_arrow.png'),
|
||||
),
|
||||
Expanded(
|
||||
child: Padding(
|
||||
@@ -62,28 +54,43 @@ class TransactionRow extends StatelessWidget {
|
||||
(isPending ? S.of(context).pending : ''),
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
color: Theme.of(context).primaryTextTheme.title.color
|
||||
)),
|
||||
Text(direction == TransactionDirection.incoming
|
||||
? formattedAmount
|
||||
: '- ' + formattedAmount,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.title
|
||||
.color)),
|
||||
Text(
|
||||
direction == TransactionDirection.incoming
|
||||
? formattedAmount
|
||||
: '- ' + formattedAmount,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
color: Theme.of(context).primaryTextTheme.title.color
|
||||
))
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.title
|
||||
.color))
|
||||
]),
|
||||
SizedBox(height: 5,),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: <Widget>[
|
||||
Text(formattedDate,
|
||||
style: TextStyle(
|
||||
fontSize: 14, color: Theme.of(context).primaryTextTheme.headline.color)),
|
||||
Text(direction == TransactionDirection.incoming
|
||||
? formattedFiatAmount
|
||||
: '- ' + formattedFiatAmount,
|
||||
fontSize: 14,
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.headline
|
||||
.color)),
|
||||
Text(
|
||||
direction == TransactionDirection.incoming
|
||||
? formattedFiatAmount
|
||||
: '- ' + formattedFiatAmount,
|
||||
style: TextStyle(
|
||||
fontSize: 14, color: Theme.of(context).primaryTextTheme.headline.color))
|
||||
fontSize: 14,
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.headline
|
||||
.color))
|
||||
]),
|
||||
],
|
||||
),
|
||||
|
||||
@@ -1,16 +1,12 @@
|
||||
import 'dart:async';
|
||||
import 'package:cake_wallet/palette.dart';
|
||||
import 'package:cake_wallet/src/domain/common/wallet_type.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:cake_wallet/src/domain/common/balance_display_mode.dart';
|
||||
import 'package:cake_wallet/src/stores/balance/balance_store.dart';
|
||||
import 'package:cake_wallet/src/stores/settings/settings_store.dart';
|
||||
import 'package:cake_wallet/src/stores/sync/sync_store.dart';
|
||||
import 'package:cake_wallet/src/stores/wallet/wallet_store.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/domain/common/sync_status.dart';
|
||||
import 'package:cake_wallet/src/screens/receive/widgets/qr_image.dart';
|
||||
@@ -20,7 +16,7 @@ import 'package:cake_wallet/view_model/dashboard_view_model.dart';
|
||||
class WalletCard extends StatefulWidget {
|
||||
WalletCard({this.walletVM});
|
||||
|
||||
DashboardViewModel walletVM;
|
||||
final DashboardViewModel walletVM;
|
||||
|
||||
@override
|
||||
WalletCardState createState() => WalletCardState();
|
||||
@@ -70,24 +66,20 @@ class WalletCardState extends State<WalletCard> {
|
||||
width: double.infinity,
|
||||
height: cardHeight,
|
||||
alignment: Alignment.centerRight,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(14), bottomLeft: Radius.circular(14))),
|
||||
child: AnimatedContainer(
|
||||
alignment: Alignment.centerLeft,
|
||||
width: cardWidth,
|
||||
height: cardHeight,
|
||||
duration: Duration(milliseconds: 500),
|
||||
curve: Curves.fastOutSlowIn,
|
||||
padding: EdgeInsets.only(top: 1, left: 1, bottom: 1),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(10),
|
||||
bottomLeft: Radius.circular(10)),
|
||||
color: Theme.of(context).focusColor,
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: PaletteDark.darkNightBlue.withOpacity(0.5),
|
||||
blurRadius: 8,
|
||||
offset: Offset(5, 5))
|
||||
]),
|
||||
topLeft: Radius.circular(14),
|
||||
bottomLeft: Radius.circular(14)),
|
||||
color: Theme.of(context).focusColor),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(10), bottomLeft: Radius.circular(10)),
|
||||
@@ -95,21 +87,18 @@ class WalletCardState extends State<WalletCard> {
|
||||
width: cardWidth,
|
||||
height: cardHeight,
|
||||
color: Theme.of(context).cardColor,
|
||||
child: InkWell(
|
||||
onTap: () => setState(() => isFrontSide = !isFrontSide),
|
||||
child: isFrontSide
|
||||
? frontSide(colorsSync)
|
||||
: backSide(colorsSync)),
|
||||
child: isFrontSide
|
||||
? frontSide(colorsSync)
|
||||
: InkWell(
|
||||
onTap: () => setState(() => isFrontSide = true),
|
||||
child: backSide(colorsSync)),
|
||||
),
|
||||
)),
|
||||
);
|
||||
}
|
||||
|
||||
Widget frontSide(List<Color> colorsSync) {
|
||||
// final syncStore = Provider.of<SyncStore>(context);
|
||||
// final walletStore = Provider.of<WalletStore>(context);
|
||||
final settingsStore = Provider.of<SettingsStore>(context);
|
||||
// final balanceStore = Provider.of<BalanceStore>(context);
|
||||
final triangleButton = Image.asset(
|
||||
'assets/images/triangle.png',
|
||||
color: Theme.of(context).primaryTextTheme.title.color,
|
||||
@@ -121,9 +110,9 @@ class WalletCardState extends State<WalletCard> {
|
||||
final status = widget.walletVM.status;
|
||||
final statusText = status.title();
|
||||
final progress = status.progress();
|
||||
final indicatorWidth = progress * cardWidth;
|
||||
final shortAddress = widget.walletVM.address
|
||||
.replaceRange(4, widget.walletVM.address.length - 4, '...');
|
||||
final indicatorOffset = progress * cardWidth;
|
||||
final indicatorWidth =
|
||||
progress <= 1 ? cardWidth - indicatorOffset : 0.0;
|
||||
var descriptionText = '';
|
||||
|
||||
if (status is SyncingSyncStatus) {
|
||||
@@ -137,37 +126,26 @@ class WalletCardState extends State<WalletCard> {
|
||||
return Container(
|
||||
width: cardWidth,
|
||||
height: cardHeight,
|
||||
color: Colors.white,
|
||||
child: Stack(
|
||||
children: <Widget>[
|
||||
Container(
|
||||
height: cardHeight,
|
||||
width: indicatorWidth,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(10),
|
||||
bottomLeft: Radius.circular(10)),
|
||||
gradient: LinearGradient(
|
||||
colors: colorsSync,
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter)),
|
||||
),
|
||||
progress != 1
|
||||
progress <= 1
|
||||
? Positioned(
|
||||
left: indicatorWidth,
|
||||
left: indicatorOffset,
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
child: Container(
|
||||
width: 1,
|
||||
width: indicatorWidth,
|
||||
height: cardHeight,
|
||||
color: Theme.of(context).focusColor,
|
||||
color: Color.fromRGBO(227, 238, 249, 1),
|
||||
))
|
||||
: Offstage(),
|
||||
isDraw
|
||||
? Positioned(
|
||||
left: 20,
|
||||
right: 20,
|
||||
top: 30,
|
||||
bottom: 30,
|
||||
left: 24,
|
||||
right: 24,
|
||||
top: 32,
|
||||
bottom: 24,
|
||||
child: Container(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
@@ -180,7 +158,8 @@ class WalletCardState extends State<WalletCard> {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
InkWell(
|
||||
onTap: () {},
|
||||
onTap: () => Navigator.of(context)
|
||||
.pushNamed(Routes.walletList),
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Text(
|
||||
@@ -199,11 +178,13 @@ class WalletCardState extends State<WalletCard> {
|
||||
),
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
if (widget.walletVM.subname?.isNotEmpty ?? false)
|
||||
if (widget.walletVM.subname?.isNotEmpty ??
|
||||
false)
|
||||
Text(
|
||||
widget.walletVM.subname,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.caption
|
||||
@@ -211,26 +192,34 @@ class WalletCardState extends State<WalletCard> {
|
||||
)
|
||||
],
|
||||
),
|
||||
Container(
|
||||
width: 98,
|
||||
height: 32,
|
||||
alignment: Alignment.center,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context)
|
||||
.accentTextTheme
|
||||
.subtitle
|
||||
.backgroundColor,
|
||||
borderRadius: BorderRadius.all(
|
||||
Radius.circular(16))),
|
||||
child: Text(
|
||||
shortAddress,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.caption
|
||||
.color),
|
||||
),
|
||||
InkWell(
|
||||
onTap: () =>
|
||||
setState(() => isFrontSide = false),
|
||||
child: Container(
|
||||
width: 98,
|
||||
height: 32,
|
||||
alignment: Alignment.center,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context)
|
||||
.accentTextTheme
|
||||
.subtitle
|
||||
.backgroundColor,
|
||||
border: Border.all(
|
||||
color: Color.fromRGBO(
|
||||
219, 231, 237, 1)),
|
||||
// FIXME
|
||||
borderRadius: BorderRadius.all(
|
||||
Radius.circular(16))),
|
||||
child: Text(
|
||||
'Receive',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.title
|
||||
.color),
|
||||
)),
|
||||
)
|
||||
],
|
||||
),
|
||||
@@ -239,7 +228,7 @@ class WalletCardState extends State<WalletCard> {
|
||||
key: _balanceObserverKey,
|
||||
builder: (_) {
|
||||
final balanceDisplayMode =
|
||||
BalanceDisplayMode.fullBalance;
|
||||
BalanceDisplayMode.availableBalance;
|
||||
// settingsStore.balanceDisplayMode;
|
||||
final symbol =
|
||||
settingsStore.fiatCurrency.toString();
|
||||
@@ -251,7 +240,7 @@ class WalletCardState extends State<WalletCard> {
|
||||
balance = widget.walletVM.balance
|
||||
.unlockedBalance ??
|
||||
'0.0';
|
||||
fiatBalance = '\$ 123.43';
|
||||
fiatBalance = '\$ 0.00';
|
||||
// '$symbol ${balanceStore.fiatUnlockedBalance}';
|
||||
}
|
||||
|
||||
@@ -260,7 +249,7 @@ class WalletCardState extends State<WalletCard> {
|
||||
balance = widget.walletVM.balance
|
||||
.totalBalance ??
|
||||
'0.0';
|
||||
fiatBalance = '\$ 123.43';
|
||||
fiatBalance = '\$ 0.00';
|
||||
// '$symbol ${balanceStore.fiatFullBalance}';
|
||||
}
|
||||
|
||||
@@ -271,6 +260,8 @@ class WalletCardState extends State<WalletCard> {
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: <Widget>[
|
||||
Column(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
@@ -284,25 +275,33 @@ class WalletCardState extends State<WalletCard> {
|
||||
.color),
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
Text(
|
||||
balance,
|
||||
style: TextStyle(
|
||||
fontSize: 28,
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.title
|
||||
.color),
|
||||
)
|
||||
Container(
|
||||
height: 36,
|
||||
child: Text(
|
||||
balance,
|
||||
style: TextStyle(
|
||||
fontSize: 32,
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.title
|
||||
.color,
|
||||
fontWeight:
|
||||
FontWeight.bold),
|
||||
))
|
||||
],
|
||||
),
|
||||
Text(
|
||||
fiatBalance,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.title
|
||||
.color),
|
||||
fontWeight: FontWeight.w600,
|
||||
// FIXME
|
||||
// color: Theme.of(context)
|
||||
// .primaryTextTheme
|
||||
// .title
|
||||
// .color,
|
||||
color: Color.fromRGBO(
|
||||
72, 89, 109, 1)),
|
||||
)
|
||||
],
|
||||
);
|
||||
@@ -320,6 +319,7 @@ class WalletCardState extends State<WalletCard> {
|
||||
statusText,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.caption
|
||||
@@ -330,6 +330,7 @@ class WalletCardState extends State<WalletCard> {
|
||||
descriptionText,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.title
|
||||
@@ -351,10 +352,8 @@ class WalletCardState extends State<WalletCard> {
|
||||
}
|
||||
|
||||
Widget backSide(List<Color> colorsSync) {
|
||||
final rightArrow = Image.asset(
|
||||
'assets/images/right_arrow.png',
|
||||
color: Theme.of(context).primaryTextTheme.title.color,
|
||||
);
|
||||
final rightArrow = Image.asset('assets/images/right_arrow.png',
|
||||
color: Theme.of(context).primaryTextTheme.title.color);
|
||||
var messageBoxHeight = 0.0;
|
||||
var messageBoxWidth = cardWidth - 10;
|
||||
|
||||
@@ -372,7 +371,7 @@ class WalletCardState extends State<WalletCard> {
|
||||
width: cardWidth,
|
||||
height: cardHeight,
|
||||
padding:
|
||||
EdgeInsets.only(left: 20, right: 20, top: 30, bottom: 30),
|
||||
EdgeInsets.only(left: 24, right: 24, top: 32, bottom: 32),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(10),
|
||||
@@ -385,10 +384,10 @@ class WalletCardState extends State<WalletCard> {
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: <Widget>[
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: Container(
|
||||
height: 90,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
@@ -426,7 +425,8 @@ class WalletCardState extends State<WalletCard> {
|
||||
child: Text(
|
||||
widget.walletVM.address,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.title
|
||||
@@ -468,9 +468,10 @@ class WalletCardState extends State<WalletCard> {
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
S.of(context).accounts_subaddresses,
|
||||
S.of(context).addresses,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.title
|
||||
|
||||
@@ -10,6 +10,7 @@ import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/src/stores/exchange_template/exchange_template_store.dart';
|
||||
import 'package:cake_wallet/src/screens/exchange/widgets/present_provider_picker.dart';
|
||||
import 'package:cake_wallet/src/screens/exchange/widgets/base_exchange_widget.dart';
|
||||
import 'package:cake_wallet/src/widgets/trail_button.dart';
|
||||
|
||||
class ExchangePage extends BasePage {
|
||||
@override
|
||||
@@ -32,20 +33,9 @@ class ExchangePage extends BasePage {
|
||||
Widget trailing(BuildContext context) {
|
||||
final exchangeStore = Provider.of<ExchangeStore>(context);
|
||||
|
||||
return ButtonTheme(
|
||||
minWidth: double.minPositive,
|
||||
highlightColor: Colors.transparent,
|
||||
splashColor: Colors.transparent,
|
||||
child: FlatButton(
|
||||
padding: EdgeInsets.all(0),
|
||||
child: Text(
|
||||
S.of(context).clear,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).primaryTextTheme.caption.color,
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 14),
|
||||
),
|
||||
onPressed: () => exchangeStore.reset()),
|
||||
return TrailButton(
|
||||
caption: S.of(context).reset,
|
||||
onPressed: () => exchangeStore.reset()
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/core/monero_account_label_validator.dart';
|
||||
import 'package:cake_wallet/view_model/monero_account_list/monero_account_edit_or_create_state.dart';
|
||||
import 'package:cake_wallet/view_model/monero_account_list/monero_account_edit_or_create_view_model.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
|
||||
|
||||
class MoneroAccountEditOrCreatePage extends BasePage {
|
||||
MoneroAccountEditOrCreatePage({@required this.moneroAccountCreationViewModel})
|
||||
: _formKey = GlobalKey<FormState>(),
|
||||
_textController = TextEditingController() {
|
||||
_textController.addListener(
|
||||
() => moneroAccountCreationViewModel.label = _textController.text);
|
||||
_textController.text = moneroAccountCreationViewModel.label;
|
||||
}
|
||||
|
||||
final MoneroAccountEditOrCreateViewModel moneroAccountCreationViewModel;
|
||||
|
||||
@override
|
||||
String get title => S.current.account;
|
||||
|
||||
final GlobalKey<FormState> _formKey;
|
||||
final TextEditingController _textController;
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) =>
|
||||
Form(
|
||||
key: _formKey,
|
||||
child: Container(
|
||||
color: Theme.of(context).backgroundColor,
|
||||
padding: EdgeInsets.all(24.0),
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: Center(
|
||||
child: BaseTextFormField(
|
||||
controller: _textController,
|
||||
hintText: S.of(context).account,
|
||||
validator: MoneroLabelValidator(),
|
||||
))),
|
||||
Observer(
|
||||
builder: (_) =>
|
||||
LoadingPrimaryButton(
|
||||
onPressed: () async {
|
||||
if (!_formKey.currentState.validate()) {
|
||||
return;
|
||||
}
|
||||
|
||||
await moneroAccountCreationViewModel.save();
|
||||
|
||||
Navigator.of(context).pop(_textController.text);
|
||||
},
|
||||
text: moneroAccountCreationViewModel.isEdit
|
||||
? S.of(context).rename
|
||||
: S.of(context).add,
|
||||
color: Colors.green,
|
||||
textColor: Colors.white,
|
||||
isLoading: moneroAccountCreationViewModel.state
|
||||
is AccountIsCreating,
|
||||
isDisabled:
|
||||
moneroAccountCreationViewModel.label?.isEmpty ?? true,
|
||||
))
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
+46
-59
@@ -1,33 +1,30 @@
|
||||
import 'dart:ui';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:cake_wallet/palette.dart';
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/stores/account_list/account_list_store.dart';
|
||||
import 'package:cake_wallet/src/screens/accounts/widgets/account_tile.dart';
|
||||
import 'package:cake_wallet/src/stores/wallet/wallet_store.dart';
|
||||
import 'package:cake_wallet/view_model/monero_account_list/monero_account_list_view_model.dart';
|
||||
import 'package:cake_wallet/src/screens/monero_accounts/widgets/account_tile.dart';
|
||||
|
||||
class AccountListPage extends StatefulWidget {
|
||||
AccountListPage({@required this.accountListStore});
|
||||
class MoneroAccountListPage extends StatefulWidget {
|
||||
MoneroAccountListPage({@required this.accountListViewModel});
|
||||
|
||||
final AccountListStore accountListStore;
|
||||
final MoneroAccountListViewModel accountListViewModel;
|
||||
|
||||
@override
|
||||
AccountListPageForm createState() => AccountListPageForm(accountListStore);
|
||||
MoneroAccountListPageForm createState() =>
|
||||
MoneroAccountListPageForm(accountListViewModel);
|
||||
}
|
||||
|
||||
class AccountListPageForm extends State<AccountListPage> {
|
||||
AccountListPageForm(this.accountListStore);
|
||||
class MoneroAccountListPageForm extends State<MoneroAccountListPage> {
|
||||
MoneroAccountListPageForm(this.accountListViewModel);
|
||||
|
||||
final AccountListStore accountListStore;
|
||||
final MoneroAccountListViewModel accountListViewModel;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final walletStore = Provider.of<WalletStore>(context);
|
||||
|
||||
return GestureDetector(
|
||||
onTap: () => Navigator.of(context).pop(),
|
||||
child: Container(
|
||||
@@ -35,7 +32,8 @@ class AccountListPageForm extends State<AccountListPage> {
|
||||
child: BackdropFilter(
|
||||
filter: ImageFilter.blur(sigmaX: 3.0, sigmaY: 3.0),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(color: PaletteDark.darkNightBlue.withOpacity(0.75)),
|
||||
decoration: BoxDecoration(
|
||||
color: PaletteDark.darkNightBlue.withOpacity(0.75)),
|
||||
child: Center(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
@@ -46,11 +44,10 @@ class AccountListPageForm extends State<AccountListPage> {
|
||||
S.of(context).choose_account,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
decoration: TextDecoration.none,
|
||||
color: Colors.white
|
||||
),
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
decoration: TextDecoration.none,
|
||||
color: Colors.white),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
@@ -61,52 +58,42 @@ class AccountListPageForm extends State<AccountListPage> {
|
||||
borderRadius: BorderRadius.all(Radius.circular(14)),
|
||||
child: Container(
|
||||
height: 296,
|
||||
color: Theme.of(context).accentTextTheme.title.backgroundColor,
|
||||
color: Theme.of(context)
|
||||
.accentTextTheme
|
||||
.title
|
||||
.backgroundColor,
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: Observer(
|
||||
builder: (_) {
|
||||
final accounts = accountListStore.accounts;
|
||||
Expanded(child: Observer(builder: (_) {
|
||||
final accounts =
|
||||
widget.accountListViewModel.accounts;
|
||||
|
||||
return ListView.separated(
|
||||
separatorBuilder: (context, index) => Divider(
|
||||
color: Theme.of(context).dividerColor,
|
||||
height: 1,
|
||||
),
|
||||
itemCount: accounts == null ? 0 : accounts.length,
|
||||
itemBuilder: (context, index) {
|
||||
final account = accounts[index];
|
||||
return ListView.separated(
|
||||
separatorBuilder: (context, index) => Divider(
|
||||
color: Theme.of(context).dividerColor,
|
||||
height: 1),
|
||||
itemCount: accounts.length ?? 0,
|
||||
itemBuilder: (context, index) {
|
||||
final account = accounts[index];
|
||||
|
||||
return Observer(
|
||||
builder: (_) {
|
||||
final isCurrent = walletStore.account.id == account.id;
|
||||
|
||||
return AccountTile(
|
||||
isCurrent: isCurrent,
|
||||
accountName: account.label,
|
||||
onTap: () {
|
||||
if (isCurrent) {
|
||||
return;
|
||||
}
|
||||
|
||||
walletStore.setAccount(account);
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
);
|
||||
return AccountTile(
|
||||
isCurrent: account.isSelected,
|
||||
accountName: account.label,
|
||||
onTap: () {
|
||||
if (account.isSelected) {
|
||||
return;
|
||||
}
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
)
|
||||
),
|
||||
|
||||
widget.accountListViewModel
|
||||
.select(account);
|
||||
Navigator.of(context).pop();
|
||||
});
|
||||
},
|
||||
);
|
||||
})),
|
||||
GestureDetector(
|
||||
onTap: () async {
|
||||
await Navigator.of(context)
|
||||
.pushNamed(Routes.accountCreation);
|
||||
accountListStore.updateAccountList();
|
||||
},
|
||||
onTap: () async => await Navigator.of(context)
|
||||
.pushNamed(Routes.accountCreation),
|
||||
child: Container(
|
||||
height: 62,
|
||||
color: Colors.white,
|
||||
@@ -1,241 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/stores/node_list/node_list_store.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
|
||||
|
||||
class NewNodePage extends BasePage {
|
||||
@override
|
||||
String get title => S.current.node_new;
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) => NewNodePageForm();
|
||||
}
|
||||
|
||||
class NewNodePageForm extends StatefulWidget {
|
||||
@override
|
||||
NewNodeFormState createState() => NewNodeFormState();
|
||||
}
|
||||
|
||||
class NewNodeFormState extends State<NewNodePageForm> {
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
final _nodeAddressController = TextEditingController();
|
||||
final _nodePortController = TextEditingController();
|
||||
final _loginController = TextEditingController();
|
||||
final _passwordController = TextEditingController();
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_nodeAddressController.dispose();
|
||||
_nodePortController.dispose();
|
||||
_loginController.dispose();
|
||||
_passwordController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void onHandleControllers(NodeListStore nodeListStore) {
|
||||
if (_nodeAddressController.text.isNotEmpty &&
|
||||
_nodePortController.text.isNotEmpty) {
|
||||
nodeListStore.setDisabledState(false);
|
||||
} else {
|
||||
nodeListStore.setDisabledState(true);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final nodeList = Provider.of<NodeListStore>(context);
|
||||
|
||||
_nodeAddressController.addListener(() {onHandleControllers(nodeList);});
|
||||
_nodePortController.addListener(() {onHandleControllers(nodeList);});
|
||||
|
||||
return Container(
|
||||
padding: EdgeInsets.only(left: 24, right: 24),
|
||||
child: ScrollableWithBottomSection(
|
||||
contentPadding: EdgeInsets.only(bottom: 24.0),
|
||||
content: Form(
|
||||
key: _formKey,
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: TextFormField(
|
||||
style: TextStyle(
|
||||
fontSize: 16.0,
|
||||
color: Theme.of(context).primaryTextTheme.title.color
|
||||
),
|
||||
decoration: InputDecoration(
|
||||
hintStyle:
|
||||
TextStyle(
|
||||
color: Theme.of(context).primaryTextTheme.caption.color,
|
||||
fontSize: 16
|
||||
),
|
||||
hintText: S.of(context).node_address,
|
||||
focusedBorder: UnderlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).dividerColor,
|
||||
width: 1.0)),
|
||||
enabledBorder: UnderlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).dividerColor,
|
||||
width: 1.0))),
|
||||
controller: _nodeAddressController,
|
||||
validator: (value) {
|
||||
nodeList.validateNodeAddress(value);
|
||||
return nodeList.errorMessage;
|
||||
},
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
SizedBox(height: 10.0),
|
||||
Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: TextFormField(
|
||||
style: TextStyle(
|
||||
fontSize: 16.0,
|
||||
color: Theme.of(context).primaryTextTheme.title.color
|
||||
),
|
||||
keyboardType: TextInputType.numberWithOptions(
|
||||
signed: false, decimal: false),
|
||||
decoration: InputDecoration(
|
||||
hintStyle:
|
||||
TextStyle(
|
||||
color: Theme.of(context).primaryTextTheme.caption.color,
|
||||
fontSize: 16
|
||||
),
|
||||
hintText: S.of(context).node_port,
|
||||
focusedBorder: UnderlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).dividerColor,
|
||||
width: 1.0)),
|
||||
enabledBorder: UnderlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).dividerColor,
|
||||
width: 1.0))),
|
||||
controller: _nodePortController,
|
||||
validator: (value) {
|
||||
nodeList.validateNodePort(value);
|
||||
return nodeList.errorMessage;
|
||||
},
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
SizedBox(height: 10.0),
|
||||
Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: TextFormField(
|
||||
style: TextStyle(
|
||||
fontSize: 16.0,
|
||||
color: Theme.of(context).primaryTextTheme.title.color
|
||||
),
|
||||
decoration: InputDecoration(
|
||||
hintStyle:
|
||||
TextStyle(
|
||||
color: Theme.of(context).primaryTextTheme.caption.color,
|
||||
fontSize: 16
|
||||
),
|
||||
hintText: S.of(context).login,
|
||||
focusedBorder: UnderlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).dividerColor,
|
||||
width: 1.0)),
|
||||
enabledBorder: UnderlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).dividerColor,
|
||||
width: 1.0))),
|
||||
controller: _loginController,
|
||||
validator: (value) => null,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
SizedBox(height: 10.0),
|
||||
Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: TextFormField(
|
||||
style: TextStyle(
|
||||
fontSize: 16.0,
|
||||
color: Theme.of(context).primaryTextTheme.title.color
|
||||
),
|
||||
decoration: InputDecoration(
|
||||
hintStyle:
|
||||
TextStyle(
|
||||
color: Theme.of(context).primaryTextTheme.caption.color,
|
||||
fontSize: 16
|
||||
),
|
||||
hintText: S.of(context).password,
|
||||
focusedBorder: UnderlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).dividerColor,
|
||||
width: 1.0)),
|
||||
enabledBorder: UnderlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).dividerColor,
|
||||
width: 1.0))),
|
||||
controller: _passwordController,
|
||||
validator: (value) => null,
|
||||
),
|
||||
)
|
||||
],
|
||||
)
|
||||
],
|
||||
)
|
||||
),
|
||||
bottomSectionPadding: EdgeInsets.only(bottom: 24),
|
||||
bottomSection: Observer(
|
||||
builder: (_) => Row(
|
||||
children: <Widget>[
|
||||
Flexible(
|
||||
child: Container(
|
||||
padding: EdgeInsets.only(right: 8.0),
|
||||
child: PrimaryButton(
|
||||
onPressed: () {
|
||||
_nodeAddressController.text = '';
|
||||
_nodePortController.text = '';
|
||||
_loginController.text = '';
|
||||
_passwordController.text = '';
|
||||
},
|
||||
text: S.of(context).reset,
|
||||
color: Colors.red,
|
||||
textColor: Colors.white),
|
||||
)),
|
||||
Flexible(
|
||||
child: Container(
|
||||
padding: EdgeInsets.only(left: 8.0),
|
||||
child: PrimaryButton(
|
||||
onPressed: () async {
|
||||
if (!_formKey.currentState.validate()) {
|
||||
return;
|
||||
}
|
||||
|
||||
await nodeList.addNode(
|
||||
address: _nodeAddressController.text,
|
||||
port: _nodePortController.text,
|
||||
login: _loginController.text,
|
||||
password: _passwordController.text);
|
||||
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
text: S.of(context).save,
|
||||
color: Colors.green,
|
||||
textColor: Colors.white,
|
||||
isDisabled: nodeList.disabledState,
|
||||
),
|
||||
)),
|
||||
],
|
||||
)
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,249 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/core/node_address_validator.dart';
|
||||
import 'package:cake_wallet/core/node_port_validator.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
|
||||
import 'package:cake_wallet/view_model/node_list/node_create_or_edit_view_model.dart';
|
||||
|
||||
class NodeCreateOrEditPage extends BasePage {
|
||||
NodeCreateOrEditPage(this.nodeCreateOrEditViewModel)
|
||||
: _formKey = GlobalKey<FormState>(),
|
||||
_addressController = TextEditingController(),
|
||||
_portController = TextEditingController(),
|
||||
_loginController = TextEditingController(),
|
||||
_passwordController = TextEditingController() {
|
||||
reaction((_) => nodeCreateOrEditViewModel.address, (String address) {
|
||||
if (address != _addressController.text) {
|
||||
_addressController.text = address;
|
||||
}
|
||||
});
|
||||
|
||||
reaction((_) => nodeCreateOrEditViewModel.port, (String port) {
|
||||
if (port != _portController.text) {
|
||||
_portController.text = port;
|
||||
}
|
||||
});
|
||||
|
||||
if (nodeCreateOrEditViewModel.hasAuthCredentials) {
|
||||
reaction((_) => nodeCreateOrEditViewModel.login, (String login) {
|
||||
if (login != _loginController.text) {
|
||||
_loginController.text = login;
|
||||
}
|
||||
});
|
||||
|
||||
reaction((_) => nodeCreateOrEditViewModel.password, (String password) {
|
||||
if (password != _passwordController.text) {
|
||||
_passwordController.text = password;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
_addressController.addListener(
|
||||
() => nodeCreateOrEditViewModel.address = _addressController.text);
|
||||
_portController.addListener(
|
||||
() => nodeCreateOrEditViewModel.port = _portController.text);
|
||||
_loginController.addListener(
|
||||
() => nodeCreateOrEditViewModel.login = _loginController.text);
|
||||
_passwordController.addListener(
|
||||
() => nodeCreateOrEditViewModel.password = _passwordController.text);
|
||||
}
|
||||
|
||||
final GlobalKey<FormState> _formKey;
|
||||
final TextEditingController _addressController;
|
||||
final TextEditingController _portController;
|
||||
final TextEditingController _loginController;
|
||||
final TextEditingController _passwordController;
|
||||
|
||||
@override
|
||||
String get title => S.current.node_new;
|
||||
|
||||
final NodeCreateOrEditViewModel nodeCreateOrEditViewModel;
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) {
|
||||
return Container(
|
||||
padding: EdgeInsets.only(left: 24, right: 24),
|
||||
child: ScrollableWithBottomSection(
|
||||
contentPadding: EdgeInsets.only(bottom: 24.0),
|
||||
content: Form(
|
||||
key: _formKey,
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: TextFormField(
|
||||
style: TextStyle(
|
||||
fontSize: 16.0,
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.title
|
||||
.color),
|
||||
decoration: InputDecoration(
|
||||
hintStyle: TextStyle(
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.caption
|
||||
.color,
|
||||
fontSize: 16),
|
||||
hintText: S.of(context).node_address,
|
||||
focusedBorder: UnderlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).dividerColor,
|
||||
width: 1.0)),
|
||||
enabledBorder: UnderlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).dividerColor,
|
||||
width: 1.0))),
|
||||
controller: _addressController,
|
||||
validator: NodeAddressValidator(),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
SizedBox(height: 10.0),
|
||||
Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: TextFormField(
|
||||
style: TextStyle(
|
||||
fontSize: 16.0,
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.title
|
||||
.color),
|
||||
keyboardType: TextInputType.numberWithOptions(
|
||||
signed: false, decimal: false),
|
||||
decoration: InputDecoration(
|
||||
hintStyle: TextStyle(
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.caption
|
||||
.color,
|
||||
fontSize: 16),
|
||||
hintText: S.of(context).node_port,
|
||||
focusedBorder: UnderlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).dividerColor,
|
||||
width: 1.0)),
|
||||
enabledBorder: UnderlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).dividerColor,
|
||||
width: 1.0))),
|
||||
controller: _portController,
|
||||
validator: NodePortValidator(),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
SizedBox(height: 10.0),
|
||||
if (nodeCreateOrEditViewModel.hasAuthCredentials) ...[
|
||||
Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: TextFormField(
|
||||
style: TextStyle(
|
||||
fontSize: 16.0,
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.title
|
||||
.color),
|
||||
decoration: InputDecoration(
|
||||
hintStyle: TextStyle(
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.caption
|
||||
.color,
|
||||
fontSize: 16),
|
||||
hintText: S.of(context).login,
|
||||
focusedBorder: UnderlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).dividerColor,
|
||||
width: 1.0)),
|
||||
enabledBorder: UnderlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).dividerColor,
|
||||
width: 1.0))),
|
||||
controller: _loginController,
|
||||
validator: (value) => null,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
SizedBox(height: 10.0),
|
||||
Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: TextFormField(
|
||||
style: TextStyle(
|
||||
fontSize: 16.0,
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.title
|
||||
.color),
|
||||
decoration: InputDecoration(
|
||||
hintStyle: TextStyle(
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.caption
|
||||
.color,
|
||||
fontSize: 16),
|
||||
hintText: S.of(context).password,
|
||||
focusedBorder: UnderlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).dividerColor,
|
||||
width: 1.0)),
|
||||
enabledBorder: UnderlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).dividerColor,
|
||||
width: 1.0))),
|
||||
controller: _passwordController,
|
||||
validator: (value) => null,
|
||||
),
|
||||
)
|
||||
],
|
||||
)
|
||||
]
|
||||
],
|
||||
)),
|
||||
bottomSectionPadding: EdgeInsets.only(bottom: 24),
|
||||
bottomSection: Observer(
|
||||
builder: (_) => Row(
|
||||
children: <Widget>[
|
||||
Flexible(
|
||||
child: Container(
|
||||
padding: EdgeInsets.only(right: 8.0),
|
||||
child: PrimaryButton(
|
||||
onPressed: () => nodeCreateOrEditViewModel.reset(),
|
||||
text: S.of(context).reset,
|
||||
color: Colors.red,
|
||||
textColor: Colors.white),
|
||||
)),
|
||||
Flexible(
|
||||
child: Container(
|
||||
padding: EdgeInsets.only(left: 8.0),
|
||||
child: PrimaryButton(
|
||||
onPressed: () async {
|
||||
if (!_formKey.currentState.validate()) {
|
||||
return;
|
||||
}
|
||||
|
||||
await nodeCreateOrEditViewModel.save();
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
text: S.of(context).save,
|
||||
color: Colors.green,
|
||||
textColor: Colors.white,
|
||||
isDisabled: !nodeCreateOrEditViewModel.isReady,
|
||||
),
|
||||
)),
|
||||
],
|
||||
)),
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -1,35 +1,30 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
import 'package:cake_wallet/palette.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/screens/nodes/widgets/node_indicator.dart';
|
||||
import 'package:cake_wallet/src/stores/node_list/node_list_store.dart';
|
||||
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/nodes/widgets/node_list_row.dart';
|
||||
import 'package:cake_wallet/src/widgets/standard_list.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
|
||||
import 'package:cake_wallet/view_model/node_list/node_list_view_model.dart';
|
||||
|
||||
class NodeListPage extends BasePage {
|
||||
NodeListPage();
|
||||
NodeListPage(this.nodeListViewModel);
|
||||
|
||||
@override
|
||||
String get title => S.current.nodes;
|
||||
|
||||
final NodeListViewModel nodeListViewModel;
|
||||
|
||||
@override
|
||||
Widget trailing(context) {
|
||||
final nodeList = Provider.of<NodeListStore>(context);
|
||||
final settings = Provider.of<SettingsStore>(context);
|
||||
|
||||
return Container(
|
||||
height: 32,
|
||||
width: 72,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.all(Radius.circular(16)),
|
||||
color: Theme.of(context).accentTextTheme.title.backgroundColor
|
||||
),
|
||||
borderRadius: BorderRadius.all(Radius.circular(16)),
|
||||
color: Theme.of(context).accentTextTheme.title.backgroundColor),
|
||||
child: ButtonTheme(
|
||||
minWidth: double.minPositive,
|
||||
child: FlatButton(
|
||||
@@ -38,24 +33,24 @@ class NodeListPage extends BasePage {
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertWithTwoActions(
|
||||
alertTitle: S.of(context).node_reset_settings_title,
|
||||
alertContent: S.of(context).nodes_list_reset_to_default_message,
|
||||
leftButtonText: S.of(context).reset,
|
||||
rightButtonText: S.of(context).cancel,
|
||||
actionLeftButton: () async {
|
||||
Navigator.of(context).pop();
|
||||
await nodeList.reset();
|
||||
await settings.setCurrentNodeToDefault();
|
||||
},
|
||||
actionRightButton: () => Navigator.of(context).pop()
|
||||
);
|
||||
alertTitle: S.of(context).node_reset_settings_title,
|
||||
alertContent:
|
||||
S.of(context).nodes_list_reset_to_default_message,
|
||||
leftButtonText: S.of(context).reset,
|
||||
rightButtonText: S.of(context).cancel,
|
||||
actionLeftButton: () async {
|
||||
Navigator.of(context).pop();
|
||||
await nodeListViewModel.reset();
|
||||
},
|
||||
actionRightButton: () => Navigator.of(context).pop());
|
||||
});
|
||||
},
|
||||
child: Text(
|
||||
S.of(context).reset,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 10.0,
|
||||
fontSize: 14.0,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.blue),
|
||||
)),
|
||||
),
|
||||
@@ -63,157 +58,77 @@ class NodeListPage extends BasePage {
|
||||
}
|
||||
|
||||
@override
|
||||
Widget body(context) => NodeListPageBody();
|
||||
}
|
||||
|
||||
class NodeListPageBody extends StatefulWidget {
|
||||
@override
|
||||
NodeListPageBodyState createState() => NodeListPageBodyState();
|
||||
}
|
||||
|
||||
class NodeListPageBodyState extends State<NodeListPageBody> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final nodeList = Provider.of<NodeListStore>(context);
|
||||
final settings = Provider.of<SettingsStore>(context);
|
||||
|
||||
final trashImage = Image.asset('assets/images/trash.png', height: 32, width: 32, color: Colors.white);
|
||||
|
||||
final currentColor = Theme.of(context).accentTextTheme.subtitle.decorationColor;
|
||||
final notCurrentColor = Theme.of(context).accentTextTheme.title.backgroundColor;
|
||||
|
||||
final currentTextColor = Colors.blue;
|
||||
final notCurrentTextColor = Theme.of(context).primaryTextTheme.title.color;
|
||||
|
||||
Widget body(context) {
|
||||
return Container(
|
||||
height: double.infinity,
|
||||
padding: EdgeInsets.only(top: 12),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: <Widget>[
|
||||
NodeListRow(
|
||||
title: S.of(context).add_new_node,
|
||||
trailing: Icon(Icons.add,
|
||||
color: Theme.of(context).primaryTextTheme.title.color,
|
||||
size: 24.0),
|
||||
color: Theme.of(context).accentTextTheme.title.backgroundColor,
|
||||
textColor: Theme.of(context).primaryTextTheme.title.color,
|
||||
onTap: () async =>
|
||||
await Navigator.of(context).pushNamed(Routes.newNode),
|
||||
isDrawTop: true,
|
||||
isDrawBottom: true),
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(top: 32),
|
||||
child: Observer(
|
||||
builder: (_) => ListView.separated(
|
||||
separatorBuilder: (_, __) => Container(
|
||||
height: 1,
|
||||
padding: EdgeInsets.only(left: 24),
|
||||
color: Theme.of(context).accentTextTheme.title.backgroundColor,
|
||||
child: Container(
|
||||
height: 1,
|
||||
color: Theme.of(context).dividerColor,
|
||||
),
|
||||
),
|
||||
itemCount: nodeList.nodes.length,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
final node = nodeList.nodes[index];
|
||||
padding: EdgeInsets.only(top: 10),
|
||||
child: Observer(
|
||||
builder: (_) => SectionStandardList(
|
||||
sectionCount: 2,
|
||||
context: context,
|
||||
itemBuilder: (_, sectionIndex, index) {
|
||||
if (sectionIndex == 0) {
|
||||
return NodeHeaderListRow(
|
||||
title: S.of(context).add_new_node,
|
||||
onTap: (_) async =>
|
||||
await Navigator.of(context).pushNamed(Routes.newNode));
|
||||
}
|
||||
|
||||
final isDrawTop = index == 0 ? true : false;
|
||||
final isDrawBottom = index == nodeList.nodes.length - 1 ? true : false;
|
||||
final node = nodeListViewModel.nodes[index];
|
||||
final isSelected = index == 1; // FIXME: hardcoded value.
|
||||
final nodeListRow = NodeListRow(
|
||||
title: node.uri,
|
||||
isSelected: isSelected,
|
||||
isAlive: node.requestNode(),
|
||||
onTap: (_) {});
|
||||
|
||||
return Observer(
|
||||
builder: (_) {
|
||||
final isCurrent = settings.node == null
|
||||
? false
|
||||
: node.key == settings.node.key;
|
||||
final dismissibleRow = Dismissible(
|
||||
key: Key('${node.key}'),
|
||||
confirmDismiss: (direction) async {
|
||||
return await showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertWithTwoActions(
|
||||
alertTitle: S.of(context).remove_node,
|
||||
alertContent: S.of(context).remove_node_message,
|
||||
leftButtonText: S.of(context).remove,
|
||||
rightButtonText: S.of(context).cancel,
|
||||
actionLeftButton: () =>
|
||||
Navigator.pop(context, true),
|
||||
actionRightButton: () =>
|
||||
Navigator.pop(context, false));
|
||||
});
|
||||
},
|
||||
onDismissed: (direction) async =>
|
||||
nodeListViewModel.delete(node),
|
||||
direction: DismissDirection.endToStart,
|
||||
background: Container(
|
||||
padding: EdgeInsets.only(right: 10.0),
|
||||
alignment: AlignmentDirectional.centerEnd,
|
||||
color: Palette.red,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: <Widget>[
|
||||
const Icon(
|
||||
CupertinoIcons.delete,
|
||||
color: Colors.white,
|
||||
),
|
||||
Text(
|
||||
S.of(context).delete,
|
||||
style: TextStyle(color: Colors.white),
|
||||
)
|
||||
],
|
||||
)),
|
||||
child: nodeListRow);
|
||||
|
||||
final content = NodeListRow(
|
||||
title: node.uri,
|
||||
trailing: FutureBuilder(
|
||||
future: nodeList.isNodeOnline(node),
|
||||
builder: (context, snapshot) {
|
||||
switch (snapshot.connectionState) {
|
||||
case ConnectionState.done:
|
||||
return NodeIndicator(
|
||||
color: snapshot.data as bool
|
||||
? Palette.green
|
||||
: Palette.red);
|
||||
default:
|
||||
return NodeIndicator();
|
||||
}
|
||||
}),
|
||||
color: isCurrent ? currentColor : notCurrentColor,
|
||||
textColor: isCurrent ? currentTextColor : notCurrentTextColor,
|
||||
onTap: () async {
|
||||
if (!isCurrent) {
|
||||
await showDialog<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertWithTwoActions(
|
||||
alertTitle: S.current.nodes,
|
||||
alertContent: S.of(context)
|
||||
.change_current_node(node.uri),
|
||||
leftButtonText: S.of(context).change,
|
||||
rightButtonText: S.of(context).cancel,
|
||||
actionLeftButton: () async {
|
||||
Navigator.of(context).pop();
|
||||
await settings.setCurrentNode(
|
||||
node: node);
|
||||
},
|
||||
actionRightButton: () => Navigator.of(context).pop()
|
||||
);
|
||||
});
|
||||
}
|
||||
},
|
||||
isDrawTop: isDrawTop,
|
||||
isDrawBottom: isDrawBottom);
|
||||
return isSelected ? nodeListRow : dismissibleRow;
|
||||
},
|
||||
itemCounter: (int sectionIndex) {
|
||||
if (sectionIndex == 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return isCurrent
|
||||
? content
|
||||
: Dismissible(
|
||||
key: Key('${node.key}'),
|
||||
confirmDismiss: (direction) async {
|
||||
return await showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertWithTwoActions(
|
||||
alertTitle: S.of(context).remove_node,
|
||||
alertContent: S.of(context).remove_node_message,
|
||||
leftButtonText: S.of(context).remove,
|
||||
rightButtonText: S.of(context).cancel,
|
||||
actionLeftButton: () =>
|
||||
Navigator.pop(context, true),
|
||||
actionRightButton: () =>
|
||||
Navigator.pop(context, false)
|
||||
);
|
||||
});
|
||||
},
|
||||
onDismissed: (direction) async =>
|
||||
await nodeList.remove(node: node),
|
||||
direction: DismissDirection.endToStart,
|
||||
background: Container(
|
||||
padding: EdgeInsets.only(right: 10.0, top: 2),
|
||||
alignment: AlignmentDirectional.centerEnd,
|
||||
color: Palette.red,
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
trashImage,
|
||||
Text(
|
||||
S.of(context).delete,
|
||||
style: TextStyle(color: Colors.white),
|
||||
)
|
||||
],
|
||||
)),
|
||||
child: content);
|
||||
},
|
||||
);
|
||||
})
|
||||
),
|
||||
)
|
||||
)
|
||||
],
|
||||
return nodeListViewModel.nodes.length;
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,16 +2,17 @@ import 'package:flutter/material.dart';
|
||||
import 'package:cake_wallet/palette.dart';
|
||||
|
||||
class NodeIndicator extends StatelessWidget {
|
||||
NodeIndicator({this.color = Palette.red});
|
||||
NodeIndicator({this.isLive = false});
|
||||
|
||||
final Color color;
|
||||
final bool isLive;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
width: 8.0,
|
||||
height: 8.0,
|
||||
decoration: BoxDecoration(shape: BoxShape.circle, color: color),
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle, color: isLive ? Palette.green : Palette.red),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,62 +1,43 @@
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/palette.dart';
|
||||
import 'package:cake_wallet/src/screens/nodes/widgets/node_indicator.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
|
||||
import 'package:cake_wallet/src/widgets/standard_list.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class NodeListRow extends StatelessWidget {
|
||||
NodeListRow({
|
||||
@required this.title,
|
||||
@required this.trailing,
|
||||
@required this.color,
|
||||
@required this.textColor,
|
||||
@required this.onTap,
|
||||
@required this.isDrawTop,
|
||||
@required this.isDrawBottom});
|
||||
class NodeListRow extends StandardListRow {
|
||||
NodeListRow(
|
||||
{@required String title,
|
||||
@required void Function(BuildContext context) onTap,
|
||||
@required bool isSelected,
|
||||
@required this.isAlive})
|
||||
: super(title: title, onTap: onTap, isSelected: isSelected);
|
||||
|
||||
final String title;
|
||||
final Widget trailing;
|
||||
final Color color;
|
||||
final Color textColor;
|
||||
final VoidCallback onTap;
|
||||
final bool isDrawTop;
|
||||
final bool isDrawBottom;
|
||||
final Future<bool> isAlive;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
children: <Widget>[
|
||||
isDrawTop
|
||||
? Container(
|
||||
width: double.infinity,
|
||||
height: 1,
|
||||
color: Theme.of(context).dividerColor,
|
||||
)
|
||||
: Offstage(),
|
||||
Container(
|
||||
width: double.infinity,
|
||||
height: 56,
|
||||
color: color,
|
||||
child: ListTile(
|
||||
contentPadding: EdgeInsets.only(
|
||||
left: 24,
|
||||
right: 24,
|
||||
),
|
||||
title: Text(
|
||||
title,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: textColor
|
||||
),
|
||||
textAlign: TextAlign.left),
|
||||
trailing: trailing,
|
||||
onTap: onTap,
|
||||
)
|
||||
),
|
||||
isDrawBottom
|
||||
? Container(
|
||||
width: double.infinity,
|
||||
height: 1,
|
||||
color: Theme.of(context).dividerColor,
|
||||
)
|
||||
: Offstage(),
|
||||
],
|
||||
);
|
||||
Widget buildTrailing(BuildContext context) {
|
||||
return FutureBuilder(
|
||||
future: isAlive,
|
||||
builder: (context, snapshot) {
|
||||
switch (snapshot.connectionState) {
|
||||
case ConnectionState.done:
|
||||
return NodeIndicator(isLive: snapshot.data as bool);
|
||||
default:
|
||||
return NodeIndicator();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class NodeHeaderListRow extends StandardListRow {
|
||||
NodeHeaderListRow({@required String title, @required void Function(BuildContext context) onTap})
|
||||
: super(title: title, onTap: onTap, isSelected: false);
|
||||
|
||||
@override
|
||||
Widget buildTrailing(BuildContext context) {
|
||||
return Icon(Icons.add,
|
||||
color: Theme.of(context).primaryTextTheme.title.color, size: 24.0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:cake_wallet/src/stores/settings/settings_store.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/domain/common/biometric_auth.dart';
|
||||
|
||||
abstract class PinCodeWidget extends StatefulWidget {
|
||||
PinCodeWidget({Key key, this.onPinCodeEntered, this.hasLengthSwitcher})
|
||||
@@ -29,6 +30,7 @@ class PinCodeState<T extends PinCodeWidget> extends State<T> {
|
||||
static const sixPinLength = 6;
|
||||
static const fourPinLength = 4;
|
||||
final _gridViewKey = GlobalKey();
|
||||
final _key = GlobalKey<ScaffoldState>();
|
||||
|
||||
int pinLength = defaultPinLength;
|
||||
List<int> pin = List<int>.filled(defaultPinLength, null);
|
||||
@@ -83,9 +85,12 @@ class PinCodeState<T extends PinCodeWidget> extends State<T> {
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => Scaffold(body: body(context));
|
||||
Widget build(BuildContext context) =>
|
||||
Scaffold(key: _key, body: body(context));
|
||||
|
||||
Widget body(BuildContext context) {
|
||||
final settingsStore = Provider.of<SettingsStore>(context);
|
||||
|
||||
final deleteIconImage = Image.asset(
|
||||
'assets/images/delete_icon.png',
|
||||
color: Theme.of(context).primaryTextTheme.title.color,
|
||||
@@ -95,8 +100,7 @@ class PinCodeState<T extends PinCodeWidget> extends State<T> {
|
||||
color: Theme.of(context).primaryTextTheme.title.color,
|
||||
);
|
||||
|
||||
return SafeArea(
|
||||
child: Container(
|
||||
return Container(
|
||||
color: Theme.of(context).backgroundColor,
|
||||
padding: EdgeInsets.only(left: 40.0, right: 40.0, bottom: 40.0),
|
||||
child: Column(children: <Widget>[
|
||||
@@ -105,8 +109,7 @@ class PinCodeState<T extends PinCodeWidget> extends State<T> {
|
||||
style: TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Theme.of(context).primaryTextTheme.title.color
|
||||
)),
|
||||
color: Theme.of(context).primaryTextTheme.title.color)),
|
||||
Spacer(flex: 3),
|
||||
Container(
|
||||
width: 180,
|
||||
@@ -138,7 +141,9 @@ class PinCodeState<T extends PinCodeWidget> extends State<T> {
|
||||
},
|
||||
child: Text(
|
||||
_changePinLengthText(),
|
||||
style: TextStyle(fontSize: 14.0, color: Theme.of(context).primaryTextTheme.caption.color),
|
||||
style: TextStyle(
|
||||
fontSize: 14.0,
|
||||
color: Theme.of(context).primaryTextTheme.caption.color),
|
||||
))
|
||||
],
|
||||
Spacer(flex: 1),
|
||||
@@ -161,10 +166,38 @@ class PinCodeState<T extends PinCodeWidget> extends State<T> {
|
||||
margin: EdgeInsets.only(
|
||||
left: marginLeft, right: marginRight),
|
||||
child: FlatButton(
|
||||
onPressed: () {},
|
||||
onPressed: (widget.hasLengthSwitcher ||
|
||||
!settingsStore
|
||||
.allowBiometricalAuthentication)
|
||||
? null
|
||||
: () {
|
||||
// FIXME
|
||||
// if (authStore != null) {
|
||||
// WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
// final biometricAuth = BiometricAuth();
|
||||
// biometricAuth.isAuthenticated().then(
|
||||
// (isAuth) {
|
||||
// if (isAuth) {
|
||||
// authStore.biometricAuth();
|
||||
// _key.currentState.showSnackBar(
|
||||
// SnackBar(
|
||||
// content: Text(S.of(context).authenticated),
|
||||
// backgroundColor: Colors.green,
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
// );
|
||||
// });
|
||||
// }
|
||||
},
|
||||
color: Theme.of(context).backgroundColor,
|
||||
shape: CircleBorder(),
|
||||
child: faceImage,
|
||||
child: (widget.hasLengthSwitcher ||
|
||||
!settingsStore
|
||||
.allowBiometricalAuthentication)
|
||||
? Offstage()
|
||||
: faceImage,
|
||||
),
|
||||
);
|
||||
} else if (index == 10) {
|
||||
@@ -195,14 +228,17 @@ class PinCodeState<T extends PinCodeWidget> extends State<T> {
|
||||
style: TextStyle(
|
||||
fontSize: 30.0,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Theme.of(context).primaryTextTheme.title.color)),
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.title
|
||||
.color)),
|
||||
),
|
||||
);
|
||||
}),
|
||||
)
|
||||
: null))
|
||||
]),
|
||||
));
|
||||
);
|
||||
}
|
||||
|
||||
void _push(int num) {
|
||||
|
||||
@@ -1,29 +1,22 @@
|
||||
import 'package:cake_wallet/palette.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:esys_flutter_share/esys_flutter_share.dart';
|
||||
import 'package:flutter_slidable/flutter_slidable.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/stores/subaddress_list/subaddress_list_store.dart';
|
||||
import 'package:cake_wallet/src/stores/wallet/wallet_store.dart';
|
||||
import 'package:cake_wallet/di.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/src/screens/receive/widgets/qr_image.dart';
|
||||
import 'package:cake_wallet/src/screens/accounts/account_list_page.dart';
|
||||
import 'package:cake_wallet/src/stores/account_list/account_list_store.dart';
|
||||
import 'package:cake_wallet/src/screens/monero_accounts/monero_account_list_page.dart';
|
||||
import 'package:cake_wallet/src/screens/receive/widgets/header_tile.dart';
|
||||
import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
|
||||
import 'package:cake_wallet/themes.dart';
|
||||
import 'package:cake_wallet/theme_changer.dart';
|
||||
import 'package:cake_wallet/core/amount_validator.dart';
|
||||
import 'package:cake_wallet/src/screens/receive/widgets/address_cell.dart';
|
||||
import 'package:cake_wallet/view_model/address_list/account_list_header.dart';
|
||||
import 'package:cake_wallet/view_model/address_list/address_list_header.dart';
|
||||
import 'package:cake_wallet/view_model/address_list/address_list_item.dart';
|
||||
import 'package:cake_wallet/view_model/address_list/address_list_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_address_list/wallet_account_list_header.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_header.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_item.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_view_model.dart';
|
||||
|
||||
class ReceivePage extends BasePage {
|
||||
ReceivePage({this.addressListViewModel})
|
||||
@@ -33,7 +26,7 @@ class ReceivePage extends BasePage {
|
||||
_formKey.currentState.validate() ? amountController.text : '');
|
||||
}
|
||||
|
||||
final AddressListViewModel addressListViewModel;
|
||||
final WalletAddressListViewModel addressListViewModel;
|
||||
final TextEditingController amountController;
|
||||
final GlobalKey<FormState> _formKey;
|
||||
|
||||
@@ -83,11 +76,6 @@ class ReceivePage extends BasePage {
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return super.build(context);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) {
|
||||
final copyImage = Image.asset('assets/images/copy_content.png',
|
||||
@@ -186,7 +174,7 @@ class ReceivePage extends BasePage {
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.title
|
||||
@@ -213,17 +201,11 @@ class ReceivePage extends BasePage {
|
||||
final item = addressListViewModel.items[index];
|
||||
Widget cell = Container();
|
||||
|
||||
if (item is AccountListHeader) {
|
||||
if (item is WalletAccountListHeader) {
|
||||
cell = HeaderTile(
|
||||
onTap: () async {
|
||||
await showDialog<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
// return AccountListPage(
|
||||
// accountListStore:
|
||||
// accountListStore);
|
||||
});
|
||||
},
|
||||
onTap: () async => await showDialog<void>(
|
||||
context: context,
|
||||
builder: (_) => getIt.get<MoneroAccountListPage>()),
|
||||
title: addressListViewModel.accountLabel,
|
||||
icon: Icon(
|
||||
Icons.arrow_forward_ios,
|
||||
@@ -233,11 +215,11 @@ class ReceivePage extends BasePage {
|
||||
));
|
||||
}
|
||||
|
||||
if (item is AddressListHeader) {
|
||||
if (item is WalletAddressListHeader) {
|
||||
cell = HeaderTile(
|
||||
onTap: () => Navigator.of(context)
|
||||
.pushNamed(Routes.newSubaddress),
|
||||
title: S.of(context).subaddresses,
|
||||
title: S.of(context).addresses,
|
||||
icon: Icon(
|
||||
Icons.add,
|
||||
size: 20,
|
||||
@@ -246,15 +228,15 @@ class ReceivePage extends BasePage {
|
||||
));
|
||||
}
|
||||
|
||||
if (item is AddressListItem) {
|
||||
if (item is WalletAddressListItem) {
|
||||
cell = Observer(
|
||||
builder: (_) => AddressCell.fromItem(item,
|
||||
isCurrent: item.address ==
|
||||
addressListViewModel.address.address,
|
||||
onTap: (_) =>
|
||||
addressListViewModel.address = item,
|
||||
onEdit: () => Navigator.of(context)
|
||||
.pushNamed(Routes.newSubaddress, arguments: item)));
|
||||
onTap: (_) => addressListViewModel.address = item,
|
||||
onEdit: () => Navigator.of(context).pushNamed(
|
||||
Routes.newSubaddress,
|
||||
arguments: item)));
|
||||
}
|
||||
|
||||
return index != 0
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_slidable/flutter_slidable.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/view_model/address_list/address_list_item.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_item.dart';
|
||||
|
||||
class AddressCell extends StatelessWidget {
|
||||
factory AddressCell.fromItem(AddressListItem item,
|
||||
factory AddressCell.fromItem(WalletAddressListItem item,
|
||||
{@required bool isCurrent,
|
||||
Function(String) onTap,
|
||||
Function() onEdit}) =>
|
||||
@@ -47,7 +47,7 @@ class AddressCell extends StatelessWidget {
|
||||
name ?? address,
|
||||
style: TextStyle(
|
||||
fontSize: name?.isNotEmpty ?? false ? 18 : 10,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: isCurrent ? currentTextColor : notCurrentTextColor,
|
||||
),
|
||||
),
|
||||
|
||||
@@ -32,7 +32,7 @@ class HeaderTile extends StatelessWidget {
|
||||
title,
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(context).primaryTextTheme.title.color
|
||||
),
|
||||
),
|
||||
|
||||
@@ -20,7 +20,6 @@ import 'package:cake_wallet/src/domain/services/wallet_service.dart';
|
||||
import 'package:cake_wallet/src/domain/exchange/trade.dart';
|
||||
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/seed/create_seed_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';
|
||||
@@ -104,13 +103,13 @@ class RootState extends State<Root> with WidgetsBindingObserver {
|
||||
|
||||
return Observer(builder: (_) {
|
||||
final state = widget.authenticationStore.state;
|
||||
print(state);
|
||||
|
||||
if (state == AuthenticationState.denied) {
|
||||
return createWelcomePage();
|
||||
}
|
||||
|
||||
if (state == AuthenticationState.installed) {
|
||||
return getIt.get<AuthPage>();
|
||||
return getIt.get<AuthPage>(instanceName: 'login');
|
||||
}
|
||||
|
||||
if (state == AuthenticationState.allowed) {
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:cake_wallet/src/domain/services/wallet_service.dart';
|
||||
import 'package:cake_wallet/src/screens/seed/seed_page.dart';
|
||||
import 'package:cake_wallet/src/stores/settings/settings_store.dart';
|
||||
import 'package:cake_wallet/src/stores/wallet_seed/wallet_seed_store.dart';
|
||||
|
||||
Widget createSeedPage(
|
||||
{@required SettingsStore settingsStore,
|
||||
@required WalletService walletService,
|
||||
@required void Function() callback}) =>
|
||||
Provider(
|
||||
create: (_) => WalletSeedStore(walletService: walletService),
|
||||
child: SeedPage(onCloseCallback: callback));
|
||||
@@ -1,161 +0,0 @@
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:esys_flutter_share/esys_flutter_share.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
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/widgets/scollable_with_bottom_section.dart';
|
||||
|
||||
class SeedPage extends BasePage {
|
||||
SeedPage({this.onCloseCallback});
|
||||
|
||||
static final image = Image.asset('assets/images/crypto_lock.png');
|
||||
|
||||
@override
|
||||
String get title => S.current.seed_title;
|
||||
|
||||
final VoidCallback onCloseCallback;
|
||||
|
||||
@override
|
||||
void onClose(BuildContext context) =>
|
||||
onCloseCallback != null ? onCloseCallback() : Navigator.of(context).pop();
|
||||
|
||||
@override
|
||||
Widget leading(BuildContext context) {
|
||||
return onCloseCallback != null ? Offstage() : super.leading(context);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget trailing(BuildContext context) {
|
||||
return onCloseCallback != null
|
||||
? GestureDetector(
|
||||
onTap: () => onClose(context),
|
||||
child: Container(
|
||||
width: 70,
|
||||
height: 32,
|
||||
alignment: Alignment.center,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.all(Radius.circular(16)),
|
||||
color: Theme.of(context).accentTextTheme.title.color
|
||||
),
|
||||
child: Text(
|
||||
S.of(context).seed_language_next,
|
||||
style: TextStyle(
|
||||
fontSize: 10,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.blue
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
: Offstage();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) {
|
||||
final walletSeedStore = Provider.of<WalletSeedStore>(context);
|
||||
String _seed;
|
||||
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
height: double.infinity,
|
||||
child: ScrollableWithBottomSection(
|
||||
contentPadding: EdgeInsets.only(left: 40, right: 40, bottom: 20),
|
||||
content: Column(
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 33),
|
||||
child: image,
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 33),
|
||||
child: Observer(
|
||||
builder: (_) {
|
||||
_seed = walletSeedStore.seed;
|
||||
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
walletSeedStore.name,
|
||||
style: TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Theme.of(context).primaryTextTheme.title.color
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 20),
|
||||
child: Text(
|
||||
_seed,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: Theme.of(context).primaryTextTheme.caption.color
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
bottomSectionPadding: EdgeInsets.only(
|
||||
left: 24,
|
||||
right: 24,
|
||||
bottom: 52
|
||||
),
|
||||
bottomSection: Container(
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: <Widget>[
|
||||
Flexible(
|
||||
child: Container(
|
||||
padding: EdgeInsets.only(right: 8.0),
|
||||
child: PrimaryButton(
|
||||
onPressed: () => Share.text(
|
||||
S.of(context).seed_share,
|
||||
_seed,
|
||||
'text/plain'),
|
||||
text: S.of(context).save,
|
||||
color: Colors.green,
|
||||
textColor: Colors.white),
|
||||
)
|
||||
),
|
||||
Flexible(
|
||||
child: Container(
|
||||
padding: EdgeInsets.only(left: 8.0),
|
||||
child: Builder(
|
||||
builder: (context) => PrimaryButton(
|
||||
onPressed: () {
|
||||
Clipboard.setData(
|
||||
ClipboardData(text: _seed));
|
||||
Scaffold.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(S
|
||||
.of(context)
|
||||
.copied_to_clipboard),
|
||||
backgroundColor: Colors.green,
|
||||
duration: Duration(milliseconds: 1500),
|
||||
),
|
||||
);
|
||||
},
|
||||
text: S.of(context).copy,
|
||||
color: Colors.blue,
|
||||
textColor: Colors.white)
|
||||
),
|
||||
)
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:esys_flutter_share/esys_flutter_share.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.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';
|
||||
import 'package:cake_wallet/themes.dart';
|
||||
import 'package:cake_wallet/theme_changer.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_seed_view_model.dart';
|
||||
|
||||
class WalletSeedPage extends BasePage {
|
||||
WalletSeedPage(this.walletSeedViewModel, {this.onCloseCallback});
|
||||
|
||||
static final imageLight = Image.asset('assets/images/crypto_lock_light.png');
|
||||
static final imageDark = Image.asset('assets/images/crypto_lock.png');
|
||||
|
||||
@override
|
||||
String get title => S.current.seed_title;
|
||||
|
||||
final VoidCallback onCloseCallback;
|
||||
final WalletSeedViewModel walletSeedViewModel;
|
||||
|
||||
@override
|
||||
void onClose(BuildContext context) =>
|
||||
onCloseCallback != null ? onCloseCallback() : Navigator.of(context).pop();
|
||||
|
||||
@override
|
||||
Widget leading(BuildContext context) =>
|
||||
onCloseCallback != null ? Offstage() : super.leading(context);
|
||||
|
||||
@override
|
||||
Widget trailing(BuildContext context) {
|
||||
return onCloseCallback != null
|
||||
? GestureDetector(
|
||||
onTap: () => onClose(context),
|
||||
child: Container(
|
||||
width: 100,
|
||||
height: 42,
|
||||
alignment: Alignment.center,
|
||||
margin: EdgeInsets.only(left: 10),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.all(Radius.circular(16)),
|
||||
color: Theme.of(context).accentTextTheme.title.color),
|
||||
child: Text(
|
||||
S.of(context).seed_language_next,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.blue),
|
||||
),
|
||||
),
|
||||
)
|
||||
: Offstage();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) {
|
||||
final _themeChanger = Provider.of<ThemeChanger>(context);
|
||||
final image =
|
||||
_themeChanger.getTheme() == Themes.darkTheme ? imageDark : imageLight;
|
||||
|
||||
return Container(
|
||||
padding: EdgeInsets.all(24),
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
Flexible(
|
||||
flex: 2,
|
||||
child: AspectRatio(
|
||||
aspectRatio: 1,
|
||||
child: FittedBox(child: image, fit: BoxFit.fill))),
|
||||
Flexible(
|
||||
flex: 3,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 33),
|
||||
child: Observer(builder: (_) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
walletSeedViewModel.name,
|
||||
style: TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.title
|
||||
.color),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 20),
|
||||
child: Text(
|
||||
walletSeedViewModel.seed,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.caption
|
||||
.color),
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
}),
|
||||
),
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: <Widget>[
|
||||
Flexible(
|
||||
child: Container(
|
||||
padding: EdgeInsets.only(right: 8.0),
|
||||
child: PrimaryButton(
|
||||
onPressed: () => Share.text(
|
||||
S.of(context).seed_share,
|
||||
walletSeedViewModel.seed,
|
||||
'text/plain'),
|
||||
text: S.of(context).save,
|
||||
color: Colors.green,
|
||||
textColor: Colors.white),
|
||||
)),
|
||||
Flexible(
|
||||
child: Container(
|
||||
padding: EdgeInsets.only(left: 8.0),
|
||||
child: Builder(
|
||||
builder: (context) => PrimaryButton(
|
||||
onPressed: () {
|
||||
Clipboard.setData(ClipboardData(
|
||||
text: walletSeedViewModel.seed));
|
||||
Scaffold.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
S.of(context).copied_to_clipboard),
|
||||
backgroundColor: Colors.green,
|
||||
duration: Duration(milliseconds: 1500),
|
||||
),
|
||||
);
|
||||
},
|
||||
text: S.of(context).copy,
|
||||
color: Colors.blue,
|
||||
textColor: Colors.white)),
|
||||
))
|
||||
],
|
||||
)
|
||||
],
|
||||
))
|
||||
],
|
||||
));
|
||||
}
|
||||
}
|
||||
+346
-341
@@ -1,5 +1,8 @@
|
||||
import 'package:cake_wallet/core/address_validator.dart';
|
||||
import 'package:cake_wallet/core/amount_validator.dart';
|
||||
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/view_model/send_view_model.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
@@ -14,7 +17,8 @@ import 'package:cake_wallet/src/stores/settings/settings_store.dart';
|
||||
import 'package:cake_wallet/src/stores/balance/balance_store.dart';
|
||||
import 'package:cake_wallet/src/stores/wallet/wallet_store.dart';
|
||||
import 'package:cake_wallet/src/stores/send/send_store.dart';
|
||||
import 'package:cake_wallet/src/stores/send/sending_state.dart';
|
||||
|
||||
//import 'package:cake_wallet/src/stores/send/sending_state.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/src/domain/common/crypto_currency.dart';
|
||||
import 'package:cake_wallet/src/domain/common/calculate_estimated_fee.dart';
|
||||
@@ -29,8 +33,13 @@ import 'package:cake_wallet/src/screens/send/widgets/confirm_sending_alert.dart'
|
||||
import 'package:cake_wallet/src/screens/send/widgets/sending_alert.dart';
|
||||
import 'package:cake_wallet/src/widgets/template_tile.dart';
|
||||
import 'package:cake_wallet/src/stores/send_template/send_template_store.dart';
|
||||
import 'package:cake_wallet/src/widgets/trail_button.dart';
|
||||
|
||||
class SendPage extends BasePage {
|
||||
SendPage({@required this.sendViewModel});
|
||||
|
||||
final SendViewModel sendViewModel;
|
||||
|
||||
@override
|
||||
String get title => S.current.send_title;
|
||||
|
||||
@@ -45,35 +54,20 @@ class SendPage extends BasePage {
|
||||
|
||||
@override
|
||||
Widget trailing(context) {
|
||||
final sendStore = Provider.of<SendStore>(context);
|
||||
// final sendStore = Provider.of<SendStore>(context);
|
||||
|
||||
return Container(
|
||||
height: 32,
|
||||
width: 82,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.all(Radius.circular(16)),
|
||||
color: Theme.of(context).accentTextTheme.title.color
|
||||
),
|
||||
child: ButtonTheme(
|
||||
minWidth: double.minPositive,
|
||||
child: FlatButton(
|
||||
onPressed: () => sendStore.clear(),
|
||||
child: Text(
|
||||
S.of(context).clear,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 10.0,
|
||||
color: Colors.blue),
|
||||
)),
|
||||
),
|
||||
);
|
||||
return TrailButton(caption: S.of(context).clear, onPressed: () => null);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) => SendForm();
|
||||
Widget body(BuildContext context) => SendForm(sendViewModel: sendViewModel);
|
||||
}
|
||||
|
||||
class SendForm extends StatefulWidget {
|
||||
SendForm({this.sendViewModel});
|
||||
|
||||
final SendViewModel sendViewModel;
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => SendFormState();
|
||||
}
|
||||
@@ -92,7 +86,7 @@ class SendFormState extends State<SendForm> {
|
||||
@override
|
||||
void initState() {
|
||||
_focusNode.addListener(() {
|
||||
if (!_focusNode.hasFocus &&_addressController.text.isNotEmpty) {
|
||||
if (!_focusNode.hasFocus && _addressController.text.isNotEmpty) {
|
||||
getOpenaliasRecord(context);
|
||||
}
|
||||
});
|
||||
@@ -102,7 +96,8 @@ class SendFormState extends State<SendForm> {
|
||||
|
||||
Future<void> getOpenaliasRecord(BuildContext context) async {
|
||||
final sendStore = Provider.of<SendStore>(context);
|
||||
final isOpenalias = await sendStore.isOpenaliasRecord(_addressController.text);
|
||||
final isOpenalias =
|
||||
await sendStore.isOpenaliasRecord(_addressController.text);
|
||||
|
||||
if (isOpenalias) {
|
||||
_addressController.text = sendStore.recordAddress;
|
||||
@@ -111,24 +106,24 @@ class SendFormState extends State<SendForm> {
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertWithOneAction(
|
||||
alertTitle: S.of(context).openalias_alert_title,
|
||||
alertContent: S.of(context).openalias_alert_content(sendStore.recordName),
|
||||
buttonText: S.of(context).ok,
|
||||
buttonAction: () => Navigator.of(context).pop()
|
||||
);
|
||||
alertTitle: S.of(context).openalias_alert_title,
|
||||
alertContent:
|
||||
S.of(context).openalias_alert_content(sendStore.recordName),
|
||||
buttonText: S.of(context).ok,
|
||||
buttonAction: () => Navigator.of(context).pop());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final settingsStore = Provider.of<SettingsStore>(context);
|
||||
final sendStore = Provider.of<SendStore>(context);
|
||||
sendStore.settingsStore = settingsStore;
|
||||
final balanceStore = Provider.of<BalanceStore>(context);
|
||||
final walletStore = Provider.of<WalletStore>(context);
|
||||
final syncStore = Provider.of<SyncStore>(context);
|
||||
final sendTemplateStore = Provider.of<SendTemplateStore>(context);
|
||||
// final settingsStore = Provider.of<SettingsStore>(context);
|
||||
// final sendStore = Provider.of<SendStore>(context);
|
||||
// sendStore.settingsStore = settingsStore;
|
||||
// final balanceStore = Provider.of<BalanceStore>(context);
|
||||
// final walletStore = Provider.of<WalletStore>(context);
|
||||
// final syncStore = Provider.of<SyncStore>(context);
|
||||
// final sendTemplateStore = Provider.of<SendTemplateStore>(context);
|
||||
|
||||
_setEffects(context);
|
||||
|
||||
@@ -166,110 +161,116 @@ class SendFormState extends State<SendForm> {
|
||||
AddressTextFieldOption.addressBook
|
||||
],
|
||||
buttonColor: Theme.of(context).accentTextTheme.title.color,
|
||||
validator: (value) {
|
||||
sendStore.validateAddress(value,
|
||||
cryptoCurrency: CryptoCurrency.xmr);
|
||||
return sendStore.errorMessage;
|
||||
},
|
||||
validator: widget.sendViewModel.addressValidator,
|
||||
),
|
||||
Observer(
|
||||
builder: (_) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(top: 20),
|
||||
child: TextFormField(
|
||||
style: TextStyle(
|
||||
fontSize: 16.0,
|
||||
color: Theme.of(context).primaryTextTheme.title.color
|
||||
),
|
||||
controller: _cryptoAmountController,
|
||||
keyboardType: TextInputType.numberWithOptions(
|
||||
signed: false, decimal: true),
|
||||
inputFormatters: [
|
||||
BlacklistingTextInputFormatter(
|
||||
RegExp('[\\-|\\ |\\,]'))
|
||||
],
|
||||
decoration: InputDecoration(
|
||||
prefixIcon: Padding(
|
||||
padding: EdgeInsets.only(top: 12),
|
||||
child: Text('XMR:',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Theme.of(context).primaryTextTheme.title.color,
|
||||
)),
|
||||
),
|
||||
suffixIcon: Padding(
|
||||
padding: EdgeInsets.only(
|
||||
bottom: 5
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: <Widget>[
|
||||
Container(
|
||||
width: MediaQuery.of(context).size.width/2,
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text(
|
||||
' / ' + balanceStore.unlockedBalance,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
color: Theme.of(context).primaryTextTheme.caption.color
|
||||
)
|
||||
Observer(builder: (_) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(top: 20),
|
||||
child: TextFormField(
|
||||
style: TextStyle(
|
||||
fontSize: 16.0,
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.title
|
||||
.color),
|
||||
controller: _cryptoAmountController,
|
||||
keyboardType: TextInputType.numberWithOptions(
|
||||
signed: false, decimal: true),
|
||||
inputFormatters: [
|
||||
BlacklistingTextInputFormatter(
|
||||
RegExp('[\\-|\\ |\\,]'))
|
||||
],
|
||||
decoration: InputDecoration(
|
||||
prefixIcon: Padding(
|
||||
padding: EdgeInsets.only(top: 12),
|
||||
child: Text('XMR:',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.title
|
||||
.color,
|
||||
)),
|
||||
),
|
||||
suffixIcon: Padding(
|
||||
padding: EdgeInsets.only(bottom: 5),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: <Widget>[
|
||||
Container(
|
||||
width:
|
||||
MediaQuery.of(context).size.width / 2,
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text(
|
||||
' / ' + widget.sendViewModel.balance,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.caption
|
||||
.color)),
|
||||
),
|
||||
Container(
|
||||
height: 32,
|
||||
width: 32,
|
||||
margin: EdgeInsets.only(
|
||||
left: 12, bottom: 7, top: 4),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context)
|
||||
.accentTextTheme
|
||||
.title
|
||||
.color,
|
||||
borderRadius: BorderRadius.all(
|
||||
Radius.circular(6))),
|
||||
child: InkWell(
|
||||
onTap: () => null,
|
||||
// widget.sendViewModel,
|
||||
child: Center(
|
||||
child: Text(S.of(context).all,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 9,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.caption
|
||||
.color)),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
height: 32,
|
||||
width: 32,
|
||||
margin: EdgeInsets.only(left: 12, bottom: 7, top: 4),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).accentTextTheme.title.color,
|
||||
borderRadius: BorderRadius.all(Radius.circular(6))
|
||||
),
|
||||
child: InkWell(
|
||||
onTap: () => sendStore.setSendAll(),
|
||||
child: Center(
|
||||
child: Text(S.of(context).all,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 9,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Theme.of(context).primaryTextTheme.caption.color
|
||||
)
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
hintStyle: TextStyle(
|
||||
fontSize: 16.0,
|
||||
color: Theme.of(context).primaryTextTheme.title.color),
|
||||
hintText: '0.0000',
|
||||
focusedBorder: UnderlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).dividerColor,
|
||||
width: 1.0)),
|
||||
enabledBorder: UnderlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).dividerColor,
|
||||
width: 1.0))),
|
||||
validator: (value) {
|
||||
sendStore.validateXMR(
|
||||
value, balanceStore.unlockedBalance);
|
||||
return sendStore.errorMessage;
|
||||
}),
|
||||
);
|
||||
}
|
||||
),
|
||||
),
|
||||
hintStyle: TextStyle(
|
||||
fontSize: 16.0,
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.title
|
||||
.color),
|
||||
hintText: '0.0000',
|
||||
focusedBorder: UnderlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).dividerColor,
|
||||
width: 1.0)),
|
||||
enabledBorder: UnderlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).dividerColor,
|
||||
width: 1.0))),
|
||||
validator: widget.sendViewModel.amountValidator),
|
||||
);
|
||||
}),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 20),
|
||||
child: TextFormField(
|
||||
style: TextStyle(
|
||||
fontSize: 16.0,
|
||||
color: Theme.of(context).primaryTextTheme.title.color),
|
||||
color:
|
||||
Theme.of(context).primaryTextTheme.title.color),
|
||||
controller: _fiatAmountController,
|
||||
keyboardType: TextInputType.numberWithOptions(
|
||||
signed: false, decimal: true),
|
||||
@@ -281,16 +282,22 @@ class SendFormState extends State<SendForm> {
|
||||
prefixIcon: Padding(
|
||||
padding: EdgeInsets.only(top: 12),
|
||||
child: Text(
|
||||
'${settingsStore.fiatCurrency.toString()}:',
|
||||
'${widget.sendViewModel.fiat.toString()}:',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Theme.of(context).primaryTextTheme.title.color,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.title
|
||||
.color,
|
||||
)),
|
||||
),
|
||||
hintStyle: TextStyle(
|
||||
fontSize: 16.0,
|
||||
color: Theme.of(context).primaryTextTheme.caption.color),
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.caption
|
||||
.color),
|
||||
hintText: '0.00',
|
||||
focusedBorder: UnderlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
@@ -310,14 +317,20 @@ class SendFormState extends State<SendForm> {
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Theme.of(context).primaryTextTheme.title.color,
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.title
|
||||
.color,
|
||||
)),
|
||||
Text(
|
||||
'${calculateEstimatedFee(priority: settingsStore.transactionPriority)} XMR',
|
||||
'${widget.sendViewModel.estimatedFee} ${widget.sendViewModel.currency.toString()}',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Theme.of(context).primaryTextTheme.title.color,
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.title
|
||||
.color,
|
||||
))
|
||||
],
|
||||
),
|
||||
@@ -325,143 +338,140 @@ class SendFormState extends State<SendForm> {
|
||||
]),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(
|
||||
top: 32,
|
||||
left: 24,
|
||||
bottom: 24
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
S.of(context).send_templates,
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Theme.of(context).primaryTextTheme.caption.color
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
Container(
|
||||
height: 40,
|
||||
width: double.infinity,
|
||||
padding: EdgeInsets.only(left: 24),
|
||||
child: Observer(
|
||||
builder: (_) {
|
||||
final itemCount = sendTemplateStore.templates.length + 1;
|
||||
|
||||
return ListView.builder(
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemCount: itemCount,
|
||||
itemBuilder: (context, index) {
|
||||
|
||||
if (index == 0) {
|
||||
return GestureDetector(
|
||||
onTap: () => Navigator.of(context)
|
||||
.pushNamed(Routes.sendTemplate),
|
||||
child: Container(
|
||||
padding: EdgeInsets.only(right: 10),
|
||||
child: DottedBorder(
|
||||
borderType: BorderType.RRect,
|
||||
dashPattern: [8, 4],
|
||||
color: Theme.of(context).accentTextTheme.title.backgroundColor,
|
||||
strokeWidth: 2,
|
||||
radius: Radius.circular(20),
|
||||
child: Container(
|
||||
height: 40,
|
||||
width: 75,
|
||||
padding: EdgeInsets.only(left: 10, right: 10),
|
||||
alignment: Alignment.center,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.all(Radius.circular(20)),
|
||||
color: Colors.transparent,
|
||||
),
|
||||
child: Text(
|
||||
S.of(context).send_new,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Theme.of(context).primaryTextTheme.caption.color
|
||||
),
|
||||
),
|
||||
)
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
index -= 1;
|
||||
|
||||
final template = sendTemplateStore.templates[index];
|
||||
|
||||
return TemplateTile(
|
||||
to: template.name,
|
||||
amount: template.amount,
|
||||
from: template.cryptoCurrency,
|
||||
onTap: () {
|
||||
_addressController.text = template.address;
|
||||
_cryptoAmountController.text = template.amount;
|
||||
getOpenaliasRecord(context);
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
),
|
||||
)
|
||||
// Padding(
|
||||
// padding: EdgeInsets.only(top: 32, left: 24, bottom: 24),
|
||||
// child: Row(
|
||||
// mainAxisAlignment: MainAxisAlignment.start,
|
||||
// children: <Widget>[
|
||||
// Text(
|
||||
// S.of(context).send_templates,
|
||||
// style: TextStyle(
|
||||
// fontSize: 18,
|
||||
// fontWeight: FontWeight.w600,
|
||||
// color:
|
||||
// Theme.of(context).primaryTextTheme.caption.color),
|
||||
// )
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// Container(
|
||||
// height: 40,
|
||||
// width: double.infinity,
|
||||
// padding: EdgeInsets.only(left: 24),
|
||||
// child: Observer(builder: (_) {
|
||||
// final itemCount = sendTemplateStore.templates.length + 1;
|
||||
//
|
||||
// return ListView.builder(
|
||||
// scrollDirection: Axis.horizontal,
|
||||
// itemCount: itemCount,
|
||||
// itemBuilder: (context, index) {
|
||||
// if (index == 0) {
|
||||
// return GestureDetector(
|
||||
// onTap: () => Navigator.of(context)
|
||||
// .pushNamed(Routes.sendTemplate),
|
||||
// child: Container(
|
||||
// padding: EdgeInsets.only(right: 10),
|
||||
// child: DottedBorder(
|
||||
// borderType: BorderType.RRect,
|
||||
// dashPattern: [8, 4],
|
||||
// color: Theme.of(context)
|
||||
// .accentTextTheme
|
||||
// .title
|
||||
// .backgroundColor,
|
||||
// strokeWidth: 2,
|
||||
// radius: Radius.circular(20),
|
||||
// child: Container(
|
||||
// height: 40,
|
||||
// width: 75,
|
||||
// padding: EdgeInsets.only(left: 10, right: 10),
|
||||
// alignment: Alignment.center,
|
||||
// decoration: BoxDecoration(
|
||||
// borderRadius:
|
||||
// BorderRadius.all(Radius.circular(20)),
|
||||
// color: Colors.transparent,
|
||||
// ),
|
||||
// child: Text(
|
||||
// S.of(context).send_new,
|
||||
// style: TextStyle(
|
||||
// fontSize: 14,
|
||||
// fontWeight: FontWeight.w600,
|
||||
// color: Theme.of(context)
|
||||
// .primaryTextTheme
|
||||
// .caption
|
||||
// .color),
|
||||
// ),
|
||||
// )),
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
//
|
||||
// index -= 1;
|
||||
//
|
||||
// final template = sendTemplateStore.templates[index];
|
||||
//
|
||||
// return TemplateTile(
|
||||
// to: template.name,
|
||||
// amount: template.amount,
|
||||
// from: template.cryptoCurrency,
|
||||
// onTap: () {
|
||||
// _addressController.text = template.address;
|
||||
// _cryptoAmountController.text = template.amount;
|
||||
// getOpenaliasRecord(context);
|
||||
// });
|
||||
// });
|
||||
// }),
|
||||
// )
|
||||
],
|
||||
),
|
||||
bottomSectionPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
|
||||
bottomSection: Observer(builder: (_) {
|
||||
return LoadingPrimaryButton(
|
||||
onPressed: syncStore.status is SyncedSyncStatus
|
||||
? () async {
|
||||
// Hack. Don't ask me.
|
||||
FocusScope.of(context).requestFocus(FocusNode());
|
||||
|
||||
if (_formKey.currentState.validate()) {
|
||||
await showDialog<void>(
|
||||
context: context,
|
||||
builder: (dialogContext) {
|
||||
return AlertWithTwoActions(
|
||||
alertTitle: S.of(context).send_creating_transaction,
|
||||
alertContent: S.of(context).confirm_sending,
|
||||
leftButtonText: S.of(context).send,
|
||||
rightButtonText: S.of(context).cancel,
|
||||
actionLeftButton: () async {
|
||||
await Navigator.of(dialogContext)
|
||||
.popAndPushNamed(Routes.auth,
|
||||
arguments: (bool
|
||||
isAuthenticatedSuccessfully,
|
||||
AuthPageState auth) {
|
||||
if (!isAuthenticatedSuccessfully) {
|
||||
return;
|
||||
}
|
||||
|
||||
Navigator.of(auth.context).pop();
|
||||
|
||||
sendStore.createTransaction(
|
||||
address: _addressController.text,
|
||||
paymentId: '');
|
||||
});
|
||||
},
|
||||
actionRightButton: () =>
|
||||
Navigator.of(context).pop()
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
: null,
|
||||
onPressed: () => null,
|
||||
// syncStore.status is SyncedSyncStatus
|
||||
// ? () async {
|
||||
// // Hack. Don't ask me.
|
||||
// FocusScope.of(context).requestFocus(FocusNode());
|
||||
//
|
||||
// if (_formKey.currentState.validate()) {
|
||||
// await showDialog<void>(
|
||||
// context: context,
|
||||
// builder: (dialogContext) {
|
||||
// return AlertWithTwoActions(
|
||||
// alertTitle:
|
||||
// S.of(context).send_creating_transaction,
|
||||
// alertContent: S.of(context).confirm_sending,
|
||||
// leftButtonText: S.of(context).send,
|
||||
// rightButtonText: S.of(context).cancel,
|
||||
// actionLeftButton: () async {
|
||||
// await Navigator.of(dialogContext)
|
||||
// .popAndPushNamed(Routes.auth, arguments:
|
||||
// (bool isAuthenticatedSuccessfully,
|
||||
// AuthPageState auth) {
|
||||
// if (!isAuthenticatedSuccessfully) {
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// Navigator.of(auth.context).pop();
|
||||
//
|
||||
// sendStore.createTransaction(
|
||||
// address: _addressController.text,
|
||||
// paymentId: '');
|
||||
// });
|
||||
// },
|
||||
// actionRightButton: () =>
|
||||
// Navigator.of(context).pop());
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
// : null,
|
||||
text: S.of(context).send,
|
||||
color: Colors.blue,
|
||||
textColor: Colors.white,
|
||||
isLoading: sendStore.state is CreatingTransaction ||
|
||||
sendStore.state is TransactionCommiting,
|
||||
isDisabled: !(syncStore.status is SyncedSyncStatus),
|
||||
);
|
||||
isLoading: widget.sendViewModel.state is TransactionIsCreating ||
|
||||
widget.sendViewModel.state is TransactionCommitting,
|
||||
isDisabled:
|
||||
false // FIXME !(syncStore.status is SyncedSyncStatus),
|
||||
);
|
||||
}),
|
||||
),
|
||||
);
|
||||
@@ -472,93 +482,88 @@ class SendFormState extends State<SendForm> {
|
||||
return;
|
||||
}
|
||||
|
||||
final sendStore = Provider.of<SendStore>(context);
|
||||
// reaction((_) => widget.sendViewModel.fiatAmount, (String amount) {
|
||||
// if (amount != _fiatAmountController.text) {
|
||||
// _fiatAmountController.text = amount;
|
||||
// }
|
||||
// });
|
||||
//
|
||||
// reaction((_) => widget.sendViewModel.cryptoAmount, (String amount) {
|
||||
// if (amount != _cryptoAmountController.text) {
|
||||
// _cryptoAmountController.text = amount;
|
||||
// }
|
||||
// });
|
||||
//
|
||||
// reaction((_) => widget.sendViewModel.address, (String address) {
|
||||
// if (address != _addressController.text) {
|
||||
// _addressController.text = address;
|
||||
// }
|
||||
// });
|
||||
//
|
||||
// _addressController.addListener(() {
|
||||
// final address = _addressController.text;
|
||||
//
|
||||
// if (widget.sendViewModel.address != address) {
|
||||
// widget.sendViewModel.changeAddress(address);
|
||||
// }
|
||||
// });
|
||||
|
||||
reaction((_) => sendStore.fiatAmount, (String amount) {
|
||||
if (amount != _fiatAmountController.text) {
|
||||
_fiatAmountController.text = amount;
|
||||
}
|
||||
});
|
||||
// _fiatAmountController.addListener(() {
|
||||
// final fiatAmount = _fiatAmountController.text;
|
||||
//
|
||||
// if (sendStore.fiatAmount != fiatAmount) {
|
||||
// sendStore.changeFiatAmount(fiatAmount);
|
||||
// }
|
||||
// });
|
||||
|
||||
reaction((_) => sendStore.cryptoAmount, (String amount) {
|
||||
if (amount != _cryptoAmountController.text) {
|
||||
_cryptoAmountController.text = amount;
|
||||
}
|
||||
});
|
||||
// _cryptoAmountController.addListener(() {
|
||||
// final cryptoAmount = _cryptoAmountController.text;
|
||||
//
|
||||
// if (sendStore.cryptoAmount != cryptoAmount) {
|
||||
// sendStore.changeCryptoAmount(cryptoAmount);
|
||||
// }
|
||||
// });
|
||||
|
||||
reaction((_) => sendStore.address, (String address) {
|
||||
if (address != _addressController.text) {
|
||||
_addressController.text = address;
|
||||
}
|
||||
});
|
||||
|
||||
_addressController.addListener(() {
|
||||
final address = _addressController.text;
|
||||
|
||||
if (sendStore.address != address) {
|
||||
sendStore.changeAddress(address);
|
||||
}
|
||||
});
|
||||
|
||||
_fiatAmountController.addListener(() {
|
||||
final fiatAmount = _fiatAmountController.text;
|
||||
|
||||
if (sendStore.fiatAmount != fiatAmount) {
|
||||
sendStore.changeFiatAmount(fiatAmount);
|
||||
}
|
||||
});
|
||||
|
||||
_cryptoAmountController.addListener(() {
|
||||
final cryptoAmount = _cryptoAmountController.text;
|
||||
|
||||
if (sendStore.cryptoAmount != cryptoAmount) {
|
||||
sendStore.changeCryptoAmount(cryptoAmount);
|
||||
}
|
||||
});
|
||||
|
||||
reaction((_) => sendStore.state, (SendingState state) {
|
||||
reaction((_) => widget.sendViewModel.state, (SendViewModelState state) {
|
||||
if (state is SendingFailed) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
showDialog<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()
|
||||
);
|
||||
alertTitle: S.of(context).error,
|
||||
alertContent: state.error,
|
||||
buttonText: S.of(context).ok,
|
||||
buttonAction: () => Navigator.of(context).pop());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (state is TransactionCreatedSuccessfully) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
showDialog<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return ConfirmSendingAlert(
|
||||
alertTitle: S.of(context).confirm_sending,
|
||||
amount: S.of(context).send_amount,
|
||||
amountValue: sendStore.pendingTransaction.amount,
|
||||
fee: S.of(context).send_fee,
|
||||
feeValue: sendStore.pendingTransaction.fee,
|
||||
leftButtonText: S.of(context).ok,
|
||||
rightButtonText: S.of(context).cancel,
|
||||
actionLeftButton: () {
|
||||
Navigator.of(context).pop();
|
||||
sendStore.commitTransaction();
|
||||
showDialog<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return SendingAlert(sendStore: sendStore);
|
||||
}
|
||||
);
|
||||
},
|
||||
actionRightButton: () => Navigator.of(context).pop()
|
||||
);
|
||||
});
|
||||
});
|
||||
// WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
// showDialog<void>(
|
||||
// context: context,
|
||||
// builder: (BuildContext context) {
|
||||
// return ConfirmSendingAlert(
|
||||
// alertTitle: S.of(context).confirm_sending,
|
||||
// amount: S.of(context).send_amount,
|
||||
// amountValue: sendStore.pendingTransaction.amount,
|
||||
// fee: S.of(context).send_fee,
|
||||
// feeValue: sendStore.pendingTransaction.fee,
|
||||
// leftButtonText: S.of(context).ok,
|
||||
// rightButtonText: S.of(context).cancel,
|
||||
// actionLeftButton: () {
|
||||
// Navigator.of(context).pop();
|
||||
// sendStore.commitTransaction();
|
||||
// showDialog<void>(
|
||||
// context: context,
|
||||
// builder: (BuildContext context) {
|
||||
// return SendingAlert(sendStore: sendStore);
|
||||
// });
|
||||
// },
|
||||
// actionRightButton: () => Navigator.of(context).pop());
|
||||
// });
|
||||
// });
|
||||
}
|
||||
|
||||
if (state is TransactionCommitted) {
|
||||
|
||||
@@ -55,7 +55,7 @@ class ConfirmSendingAlert extends BaseAlertDialog {
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.white,
|
||||
color: Theme.of(context).primaryTextTheme.title.color,
|
||||
decoration: TextDecoration.none,
|
||||
),
|
||||
),
|
||||
@@ -64,7 +64,7 @@ class ConfirmSendingAlert extends BaseAlertDialog {
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.white,
|
||||
color: Theme.of(context).primaryTextTheme.title.color,
|
||||
decoration: TextDecoration.none,
|
||||
),
|
||||
)
|
||||
@@ -79,7 +79,7 @@ class ConfirmSendingAlert extends BaseAlertDialog {
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.white,
|
||||
color: Theme.of(context).primaryTextTheme.title.color,
|
||||
decoration: TextDecoration.none,
|
||||
),
|
||||
),
|
||||
@@ -88,7 +88,7 @@ class ConfirmSendingAlert extends BaseAlertDialog {
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.white,
|
||||
color: Theme.of(context).primaryTextTheme.title.color,
|
||||
decoration: TextDecoration.none,
|
||||
),
|
||||
)
|
||||
|
||||
@@ -1,438 +1,72 @@
|
||||
import 'package:cake_wallet/src/screens/auth/auth_page.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/domain/common/balance_display_mode.dart';
|
||||
import 'package:cake_wallet/src/domain/common/fiat_currency.dart';
|
||||
import 'package:cake_wallet/src/domain/common/transaction_priority.dart';
|
||||
import 'package:cake_wallet/src/stores/settings/settings_store.dart';
|
||||
import 'package:cake_wallet/src/stores/action_list/action_list_display_mode.dart';
|
||||
import 'package:cake_wallet/view_model/settings/settings_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/settings/link_list_item.dart';
|
||||
import 'package:cake_wallet/view_model/settings/picker_list_item.dart';
|
||||
import 'package:cake_wallet/view_model/settings/regular_list_item.dart';
|
||||
import 'package:cake_wallet/view_model/settings/switcher_list_item.dart';
|
||||
import 'package:cake_wallet/src/screens/settings/widgets/settings_link_provider_cell.dart';
|
||||
import 'package:cake_wallet/src/screens/settings/widgets/settings_cell_with_arrow.dart';
|
||||
import 'package:cake_wallet/src/screens/settings/widgets/settings_picker_cell.dart';
|
||||
import 'package:cake_wallet/src/screens/settings/widgets/settings_switcher_cell.dart';
|
||||
import 'package:cake_wallet/src/widgets/standard_list.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/src/screens/settings/attributes.dart';
|
||||
import 'package:cake_wallet/src/screens/disclaimer/disclaimer_page.dart';
|
||||
import 'package:cake_wallet/src/screens/settings/items/settings_item.dart';
|
||||
import 'package:cake_wallet/src/screens/settings/items/item_headers.dart';
|
||||
import 'package:cake_wallet/src/widgets/picker.dart';
|
||||
// Settings widgets
|
||||
import 'package:cake_wallet/src/screens/settings/widgets/settings_arrow_list_row.dart';
|
||||
import 'package:cake_wallet/src/screens/settings/widgets/settings_header_list_row.dart';
|
||||
import 'package:cake_wallet/src/screens/settings/widgets/settings_link_list_row.dart';
|
||||
import 'package:cake_wallet/src/screens/settings/widgets/settings_switch_list_row.dart';
|
||||
import 'package:cake_wallet/src/screens/settings/widgets/settings_text_list_row.dart';
|
||||
import 'package:cake_wallet/src/screens/settings/widgets/settings_raw_widget_list_row.dart';
|
||||
|
||||
class SettingsPage extends BasePage {
|
||||
SettingsPage(this.settingsViewModel);
|
||||
|
||||
final SettingsViewModel settingsViewModel;
|
||||
|
||||
@override
|
||||
String get title => S.current.settings_title;
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) {
|
||||
return SettingsForm();
|
||||
}
|
||||
}
|
||||
|
||||
class SettingsForm extends StatefulWidget {
|
||||
@override
|
||||
SettingsFormState createState() => SettingsFormState();
|
||||
}
|
||||
|
||||
class SettingsFormState extends State<SettingsForm> {
|
||||
final _telegramImage = Image.asset('assets/images/Telegram.png');
|
||||
final _twitterImage = Image.asset('assets/images/Twitter.png');
|
||||
final _changeNowImage = Image.asset('assets/images/change_now.png');
|
||||
final _xmrBtcImage = Image.asset('assets/images/xmr_btc.png');
|
||||
final _morphImage = Image.asset('assets/images/morph_icon.png');
|
||||
|
||||
final _emailUrl = 'mailto:support@cakewallet.com';
|
||||
final _telegramUrl = 'https:t.me/cakewallet_bot';
|
||||
final _twitterUrl = 'https:twitter.com/CakewalletXMR';
|
||||
final _changeNowUrl = 'mailto:support@changenow.io';
|
||||
final _xmrToUrl = 'mailto:support@xmr.to';
|
||||
final _morphUrl = 'mailto:support@morphtoken.com';
|
||||
|
||||
final _items = List<SettingsItem>();
|
||||
|
||||
void _launchUrl(String url) async {
|
||||
if (await canLaunch(url)) await launch(url);
|
||||
}
|
||||
|
||||
void _setSettingsList() {
|
||||
final settingsStore = Provider.of<SettingsStore>(context);
|
||||
|
||||
settingsStore.setItemHeaders();
|
||||
|
||||
_items.addAll([
|
||||
SettingsItem(
|
||||
onTaped: () => _setBalance(context),
|
||||
title: ItemHeaders.displayBalanceAs,
|
||||
widget: Observer(
|
||||
builder: (_) => Text(
|
||||
settingsStore.balanceDisplayMode.toString(),
|
||||
textAlign: TextAlign.right,
|
||||
style: TextStyle(
|
||||
fontSize: 14.0,
|
||||
color: Theme.of(context).primaryTextTheme.caption.color),
|
||||
)),
|
||||
attribute: Attributes.widget),
|
||||
SettingsItem(
|
||||
onTaped: () => _setCurrency(context),
|
||||
title: ItemHeaders.currency,
|
||||
widget: Observer(
|
||||
builder: (_) => Text(
|
||||
settingsStore.fiatCurrency.toString(),
|
||||
textAlign: TextAlign.right,
|
||||
style: TextStyle(
|
||||
fontSize: 14.0,
|
||||
color: Theme.of(context).primaryTextTheme.caption.color),
|
||||
)),
|
||||
attribute: Attributes.widget),
|
||||
SettingsItem(
|
||||
onTaped: () => _setTransactionPriority(context),
|
||||
title: ItemHeaders.feePriority,
|
||||
widget: Observer(
|
||||
builder: (_) => Text(
|
||||
settingsStore.transactionPriority.toString(),
|
||||
textAlign: TextAlign.right,
|
||||
style: TextStyle(
|
||||
fontSize: 14.0,
|
||||
color: Theme.of(context).primaryTextTheme.caption.color),
|
||||
)),
|
||||
attribute: Attributes.widget),
|
||||
SettingsItem(
|
||||
title: ItemHeaders.saveRecipientAddress,
|
||||
attribute: Attributes.switcher),
|
||||
SettingsItem(title: '', attribute: Attributes.header),
|
||||
SettingsItem(
|
||||
onTaped: () {
|
||||
Navigator.of(context).pushNamed(Routes.auth,
|
||||
arguments: (bool isAuthenticatedSuccessfully,
|
||||
AuthPageState auth) =>
|
||||
isAuthenticatedSuccessfully
|
||||
? Navigator.of(context).popAndPushNamed(Routes.setupPin,
|
||||
arguments:
|
||||
(BuildContext setupPinContext, String _) =>
|
||||
Navigator.of(context).pop())
|
||||
: null);
|
||||
},
|
||||
title: ItemHeaders.changePIN,
|
||||
attribute: Attributes.arrow),
|
||||
SettingsItem(
|
||||
onTaped: () => Navigator.pushNamed(context, Routes.changeLanguage),
|
||||
title: ItemHeaders.changeLanguage,
|
||||
attribute: Attributes.arrow),
|
||||
SettingsItem(
|
||||
title: ItemHeaders.allowBiometricalAuthentication,
|
||||
attribute: Attributes.switcher),
|
||||
SettingsItem(title: ItemHeaders.darkMode, attribute: Attributes.switcher),
|
||||
SettingsItem(
|
||||
widgetBuilder: (context) {
|
||||
return PopupMenuButton<ActionListDisplayMode>(
|
||||
itemBuilder: (context) => [
|
||||
PopupMenuItem(
|
||||
value: ActionListDisplayMode.transactions,
|
||||
child: Observer(
|
||||
builder: (_) => Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(S
|
||||
.of(context)
|
||||
.settings_transactions,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).primaryTextTheme.title.color
|
||||
),
|
||||
),
|
||||
Checkbox(
|
||||
value: settingsStore
|
||||
.actionlistDisplayMode
|
||||
.contains(ActionListDisplayMode
|
||||
.transactions),
|
||||
onChanged: (value) => settingsStore
|
||||
.toggleTransactionsDisplay(),
|
||||
)
|
||||
]))),
|
||||
PopupMenuItem(
|
||||
value: ActionListDisplayMode.trades,
|
||||
child: Observer(
|
||||
builder: (_) => Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
S.of(context).settings_trades,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).primaryTextTheme.title.color
|
||||
),
|
||||
),
|
||||
Checkbox(
|
||||
value: settingsStore
|
||||
.actionlistDisplayMode
|
||||
.contains(
|
||||
ActionListDisplayMode.trades),
|
||||
onChanged: (value) => settingsStore
|
||||
.toggleTradesDisplay(),
|
||||
)
|
||||
])))
|
||||
],
|
||||
child: Container(
|
||||
height: 56,
|
||||
padding: EdgeInsets.only(left: 24, right: 24),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(S.of(context).settings_display_on_dashboard_list,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: Theme.of(context).primaryTextTheme.title.color)),
|
||||
Observer(builder: (_) {
|
||||
var title = '';
|
||||
|
||||
if (settingsStore.actionlistDisplayMode.length ==
|
||||
ActionListDisplayMode.values.length) {
|
||||
title = S.of(context).settings_all;
|
||||
}
|
||||
|
||||
if (title.isEmpty &&
|
||||
settingsStore.actionlistDisplayMode
|
||||
.contains(ActionListDisplayMode.trades)) {
|
||||
title = S.of(context).settings_only_trades;
|
||||
}
|
||||
|
||||
if (title.isEmpty &&
|
||||
settingsStore.actionlistDisplayMode.contains(
|
||||
ActionListDisplayMode.transactions)) {
|
||||
title = S.of(context).settings_only_transactions;
|
||||
}
|
||||
|
||||
if (title.isEmpty) {
|
||||
title = S.of(context).settings_none;
|
||||
}
|
||||
|
||||
return Text(title,
|
||||
style: TextStyle(
|
||||
fontSize: 14.0,
|
||||
color: Theme.of(context).primaryTextTheme.caption.color));
|
||||
})
|
||||
]),
|
||||
));
|
||||
},
|
||||
attribute: Attributes.rawWidget),
|
||||
SettingsItem(title: '', attribute: Attributes.header),
|
||||
SettingsItem(
|
||||
onTaped: () => _launchUrl(_emailUrl),
|
||||
title: 'Email',
|
||||
link: 'support@cakewallet.com',
|
||||
image: null,
|
||||
attribute: Attributes.link),
|
||||
SettingsItem(
|
||||
onTaped: () => _launchUrl(_telegramUrl),
|
||||
title: 'Telegram',
|
||||
link: 'Cake_Wallet',
|
||||
image: _telegramImage,
|
||||
attribute: Attributes.link),
|
||||
SettingsItem(
|
||||
onTaped: () => _launchUrl(_twitterUrl),
|
||||
title: 'Twitter',
|
||||
link: '@CakeWalletXMR',
|
||||
image: _twitterImage,
|
||||
attribute: Attributes.link),
|
||||
SettingsItem(
|
||||
onTaped: () => _launchUrl(_changeNowUrl),
|
||||
title: 'ChangeNow',
|
||||
link: 'support@changenow.io',
|
||||
image: _changeNowImage,
|
||||
attribute: Attributes.link),
|
||||
SettingsItem(
|
||||
onTaped: () => _launchUrl(_morphUrl),
|
||||
title: 'Morph',
|
||||
link: 'support@morphtoken.com',
|
||||
image: _morphImage,
|
||||
attribute: Attributes.link),
|
||||
SettingsItem(
|
||||
onTaped: () => _launchUrl(_xmrToUrl),
|
||||
title: 'XMR.to',
|
||||
link: 'support@xmr.to',
|
||||
image: _xmrBtcImage,
|
||||
attribute: Attributes.link),
|
||||
SettingsItem(
|
||||
onTaped: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
CupertinoPageRoute<void>(
|
||||
builder: (BuildContext context) => DisclaimerPage()));
|
||||
},
|
||||
title: ItemHeaders.termsAndConditions,
|
||||
attribute: Attributes.arrow),
|
||||
SettingsItem(
|
||||
onTaped: () => Navigator.pushNamed(context, Routes.faq),
|
||||
title: ItemHeaders.faq,
|
||||
attribute: Attributes.arrow)
|
||||
]);
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
void _afterLayout(dynamic _) => _setSettingsList();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
WidgetsBinding.instance.addPostFrameCallback(_afterLayout);
|
||||
}
|
||||
|
||||
Widget _getWidget(SettingsItem item) {
|
||||
switch (item.attribute) {
|
||||
case Attributes.arrow:
|
||||
return SettingsArrowListRow(
|
||||
onTaped: item.onTaped,
|
||||
title: item.title,
|
||||
);
|
||||
case Attributes.header:
|
||||
return SettingsHeaderListRow(
|
||||
title: item.title,
|
||||
);
|
||||
case Attributes.link:
|
||||
return SettingsLinktListRow(
|
||||
onTaped: item.onTaped,
|
||||
title: item.title,
|
||||
link: item.link,
|
||||
image: item.image,
|
||||
);
|
||||
case Attributes.switcher:
|
||||
return SettingsSwitchListRow(
|
||||
title: item.title,
|
||||
);
|
||||
case Attributes.widget:
|
||||
return SettingsTextListRow(
|
||||
onTaped: item.onTaped,
|
||||
title: item.title,
|
||||
widget: item.widget,
|
||||
);
|
||||
case Attributes.rawWidget:
|
||||
return SettingRawWidgetListRow(widgetBuilder: item.widgetBuilder);
|
||||
default:
|
||||
return Offstage();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final settingsStore = Provider.of<SettingsStore>(context);
|
||||
settingsStore.setItemHeaders();
|
||||
|
||||
final shortDivider = Container(
|
||||
height: 1,
|
||||
padding: EdgeInsets.only(left: 24),
|
||||
color: Theme.of(context).accentTextTheme.title.backgroundColor,
|
||||
child: Container(
|
||||
height: 1,
|
||||
color: Theme.of(context).dividerColor,
|
||||
),
|
||||
);
|
||||
|
||||
final longDivider = Container(
|
||||
height: 1,
|
||||
color: Theme.of(context).dividerColor,
|
||||
);
|
||||
|
||||
return Container(
|
||||
padding: EdgeInsets.only(top: 12),
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
longDivider,
|
||||
ListView.builder(
|
||||
shrinkWrap: true,
|
||||
physics: NeverScrollableScrollPhysics(),
|
||||
itemCount: _items.length,
|
||||
itemBuilder: (context, index) {
|
||||
final item = _items[index];
|
||||
|
||||
Widget divider;
|
||||
|
||||
if (item.attribute == Attributes.header || item == _items.last) {
|
||||
divider = longDivider;
|
||||
} else if (_items[index + 1].attribute == Attributes.header){
|
||||
divider = longDivider;
|
||||
} else {
|
||||
divider = shortDivider;
|
||||
}
|
||||
|
||||
return Column(
|
||||
children: <Widget>[
|
||||
_getWidget(item),
|
||||
divider
|
||||
],
|
||||
);
|
||||
}),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 12),
|
||||
child: ListTile(
|
||||
title: Center(
|
||||
child: Text(
|
||||
settingsStore.itemHeaders[ItemHeaders.version],
|
||||
style: TextStyle(
|
||||
fontSize: 14.0,
|
||||
color: Theme.of(context).primaryTextTheme.caption.color)
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
)),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _setBalance(BuildContext context) async {
|
||||
final settingsStore = Provider.of<SettingsStore>(context);
|
||||
final items = BalanceDisplayMode.all;
|
||||
final selectedItem = items.indexOf(settingsStore.balanceDisplayMode);
|
||||
|
||||
await showDialog<void>(
|
||||
builder: (_) => Picker(
|
||||
items: items,
|
||||
selectedAtIndex: selectedItem,
|
||||
title: S.of(context).please_select,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
onItemSelected: (BalanceDisplayMode mode) async =>
|
||||
await settingsStore.setCurrentBalanceDisplayMode(
|
||||
balanceDisplayMode: mode)),
|
||||
context: context);
|
||||
}
|
||||
|
||||
Future<void> _setCurrency(BuildContext context) async {
|
||||
final settingsStore = Provider.of<SettingsStore>(context);
|
||||
final items = FiatCurrency.all;
|
||||
final selectedItem = items.indexOf(settingsStore.fiatCurrency);
|
||||
|
||||
await showDialog<void>(
|
||||
builder: (_) => Picker(
|
||||
items: items,
|
||||
selectedAtIndex: selectedItem,
|
||||
title: S.of(context).please_select,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
onItemSelected: (FiatCurrency currency) async =>
|
||||
await settingsStore.setCurrentFiatCurrency(currency: currency)),
|
||||
context: context);
|
||||
}
|
||||
|
||||
Future<void> _setTransactionPriority(BuildContext context) async {
|
||||
final settingsStore = Provider.of<SettingsStore>(context);
|
||||
final items = TransactionPriority.all;
|
||||
final selectedItem = items.indexOf(settingsStore.transactionPriority);
|
||||
|
||||
await showDialog<void>(
|
||||
builder: (_) => Picker(
|
||||
items: items,
|
||||
selectedAtIndex: selectedItem,
|
||||
title: S.of(context).please_select,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
onItemSelected: (TransactionPriority priority) async =>
|
||||
await settingsStore.setCurrentTransactionPriority(
|
||||
priority: priority)),
|
||||
context: context);
|
||||
return SectionStandardList(
|
||||
sectionCount: settingsViewModel.sections.length,
|
||||
itemCounter: (int sectionIndex) {
|
||||
if (sectionIndex < settingsViewModel.sections.length) {
|
||||
return settingsViewModel.sections[sectionIndex].length;
|
||||
}
|
||||
|
||||
return 0;
|
||||
},
|
||||
itemBuilder: (_, sectionIndex, itemIndex) {
|
||||
final item = settingsViewModel.sections[sectionIndex][itemIndex];
|
||||
|
||||
if (item is PickerListItem) {
|
||||
return Observer(builder: (_) {
|
||||
return SettingsPickerCell<dynamic>(
|
||||
title: item.title,
|
||||
selectedItem: item.selectedItem(),
|
||||
items: item.items);
|
||||
});
|
||||
}
|
||||
|
||||
if (item is SwitcherListItem) {
|
||||
return Observer(builder: (_) {
|
||||
return SettingsSwitcherCell(
|
||||
title: item.title,
|
||||
value: item.value(),
|
||||
onValueChange: item.onValueChange);
|
||||
});
|
||||
}
|
||||
|
||||
if (item is RegularListItem) {
|
||||
return SettingsCellWithArrow(title: item.title);
|
||||
}
|
||||
|
||||
if (item is LinkListItem) {
|
||||
return SettingsLinkProviderCell(
|
||||
title: item.title,
|
||||
icon: item.icon,
|
||||
link: item.link,
|
||||
linkTitle: item.linkTitle);
|
||||
}
|
||||
|
||||
return Container();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:cake_wallet/src/stores/settings/settings_store.dart';
|
||||
|
||||
class SettingsArrowListRow extends StatelessWidget {
|
||||
SettingsArrowListRow({@required this.onTaped, this.title});
|
||||
|
||||
final VoidCallback onTaped;
|
||||
final String title;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final settingsStore = Provider.of<SettingsStore>(context);
|
||||
final _cakeArrowImage = Image.asset('assets/images/select_arrow.png',
|
||||
color: Theme.of(context).primaryTextTheme.caption.color);
|
||||
|
||||
return Container(
|
||||
color: Theme.of(context).accentTextTheme.title.backgroundColor,
|
||||
child: ListTile(
|
||||
contentPadding: EdgeInsets.only(left: 24.0, right: 24.0),
|
||||
title: Observer(
|
||||
builder: (_) => Text(
|
||||
settingsStore.itemHeaders[title],
|
||||
style: TextStyle(
|
||||
fontSize: 14.0,
|
||||
color: Theme.of(context).primaryTextTheme.title.color),
|
||||
)),
|
||||
trailing: _cakeArrowImage,
|
||||
onTap: onTaped),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:cake_wallet/src/widgets/standard_list.dart';
|
||||
|
||||
class SettingsCellWithArrow extends StandardListRow {
|
||||
SettingsCellWithArrow({@required String title})
|
||||
: super(title: title, isSelected: false);
|
||||
|
||||
@override
|
||||
Widget buildTrailing(BuildContext context) =>
|
||||
Image.asset('assets/images/select_arrow.png',
|
||||
color: Theme.of(context).primaryTextTheme.caption.color);
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:cake_wallet/src/stores/settings/settings_store.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class SettingsHeaderListRow extends StatelessWidget {
|
||||
SettingsHeaderListRow({this.title});
|
||||
|
||||
final String title;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final settingsStore = Provider.of<SettingsStore>(context);
|
||||
|
||||
return Column(
|
||||
children: <Widget>[
|
||||
SizedBox(
|
||||
height: 20.0,
|
||||
),
|
||||
Container(
|
||||
padding: EdgeInsets.only(left: 20.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Observer(
|
||||
builder: (_) => Text(
|
||||
title.isNotEmpty
|
||||
? settingsStore.itemHeaders[title]
|
||||
: '',
|
||||
style: TextStyle(
|
||||
fontSize: 15.0,
|
||||
color: Theme.of(context).primaryTextTheme.caption.color),
|
||||
))
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height: 14.0,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class SettingsLinktListRow extends StatelessWidget {
|
||||
SettingsLinktListRow(
|
||||
{@required this.onTaped, this.title, this.link, this.image});
|
||||
|
||||
final VoidCallback onTaped;
|
||||
final String title;
|
||||
final String link;
|
||||
final Image image;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
color: Theme.of(context).accentTextTheme.title.backgroundColor,
|
||||
child: ListTile(
|
||||
contentPadding: EdgeInsets.only(left: 24.0, right: 24.0),
|
||||
title: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
image != null ? image : Offstage(),
|
||||
Container(
|
||||
padding: image != null ? EdgeInsets.only(left: 10) : null,
|
||||
child: Text(
|
||||
title,
|
||||
style: TextStyle(
|
||||
fontSize: 14.0,
|
||||
color: Theme.of(context).primaryTextTheme.title.color),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
trailing: Text(
|
||||
link,
|
||||
style: TextStyle(fontSize: 14.0, color: Colors.blue),
|
||||
),
|
||||
onTap: onTaped,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:cake_wallet/src/widgets/standard_list.dart';
|
||||
|
||||
class SettingsLinkProviderCell extends StandardListRow {
|
||||
SettingsLinkProviderCell(
|
||||
{@required String title,
|
||||
@required this.icon,
|
||||
@required this.link,
|
||||
@required this.linkTitle})
|
||||
: super(title: title, isSelected: false);
|
||||
|
||||
final String icon;
|
||||
final String link;
|
||||
final String linkTitle;
|
||||
|
||||
@override
|
||||
Widget buildLeading(BuildContext context) =>
|
||||
icon != null ? Image.asset(icon) : null;
|
||||
|
||||
@override
|
||||
Widget buildTrailing(BuildContext context) => Text(linkTitle,
|
||||
style: TextStyle(
|
||||
fontSize: 14.0, fontWeight: FontWeight.w500, color: Colors.blue));
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:cake_wallet/src/widgets/picker.dart';
|
||||
import 'package:cake_wallet/src/widgets/standard_list.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
|
||||
class SettingsPickerCell<ItemType> extends StandardListRow {
|
||||
SettingsPickerCell({@required String title, this.selectedItem, this.items})
|
||||
: super(
|
||||
title: title,
|
||||
isSelected: false,
|
||||
onTap: (BuildContext context) async {
|
||||
final selectedAtIndex = items.indexOf(selectedItem);
|
||||
|
||||
await showDialog<void>(
|
||||
context: context,
|
||||
builder: (_) => Picker(
|
||||
items: items,
|
||||
selectedAtIndex: selectedAtIndex,
|
||||
title: S.current.please_select,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
onItemSelected: (Object _) {}));
|
||||
});
|
||||
|
||||
final ItemType selectedItem;
|
||||
final List<ItemType> items;
|
||||
|
||||
@override
|
||||
Widget buildTrailing(BuildContext context) {
|
||||
return Text(
|
||||
selectedItem.toString(),
|
||||
textAlign: TextAlign.right,
|
||||
style: TextStyle(
|
||||
fontSize: 14.0,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(context).primaryTextTheme.caption.color),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
|
||||
class SettingsPickerRaw extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// TODO: implement build
|
||||
throw UnimplementedError();
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class SettingRawWidgetListRow extends StatelessWidget {
|
||||
SettingRawWidgetListRow({@required this.widgetBuilder});
|
||||
|
||||
final WidgetBuilder widgetBuilder;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
color: Theme.of(context).accentTextTheme.title.backgroundColor,
|
||||
child: widgetBuilder(context) ?? Container(),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/theme_changer.dart';
|
||||
import 'package:cake_wallet/themes.dart';
|
||||
import 'package:cake_wallet/src/stores/settings/settings_store.dart';
|
||||
import 'package:cake_wallet/src/widgets/standart_switch.dart';
|
||||
|
||||
class SettingsSwitchListRow extends StatelessWidget {
|
||||
SettingsSwitchListRow({@required this.title});
|
||||
|
||||
final String title;
|
||||
|
||||
Widget _getSwitch(BuildContext context) {
|
||||
final settingsStore = Provider.of<SettingsStore>(context);
|
||||
final _themeChanger = Provider.of<ThemeChanger>(context);
|
||||
|
||||
if (settingsStore.itemHeaders[title] ==
|
||||
S.of(context).settings_save_recipient_address) {
|
||||
return Observer(
|
||||
builder: (_) => StandartSwitch(
|
||||
value: settingsStore.shouldSaveRecipientAddress,
|
||||
onTaped: () {
|
||||
final _currentValue = !settingsStore.shouldSaveRecipientAddress;
|
||||
settingsStore.setSaveRecipientAddress(
|
||||
shouldSaveRecipientAddress: _currentValue);
|
||||
}));
|
||||
}
|
||||
|
||||
if (settingsStore.itemHeaders[title] ==
|
||||
S.of(context).settings_allow_biometrical_authentication) {
|
||||
return Observer(
|
||||
builder: (_) => StandartSwitch(
|
||||
value: settingsStore.allowBiometricalAuthentication,
|
||||
onTaped: () {
|
||||
final _currentValue =
|
||||
!settingsStore.allowBiometricalAuthentication;
|
||||
settingsStore.setAllowBiometricalAuthentication(
|
||||
allowBiometricalAuthentication: _currentValue);
|
||||
}));
|
||||
}
|
||||
|
||||
if (settingsStore.itemHeaders[title] == S.of(context).settings_dark_mode) {
|
||||
return Observer(
|
||||
builder: (_) => StandartSwitch(
|
||||
value: settingsStore.isDarkTheme,
|
||||
onTaped: () {
|
||||
final _currentValue = !settingsStore.isDarkTheme;
|
||||
settingsStore.saveDarkTheme(isDarkTheme: _currentValue);
|
||||
_themeChanger.setTheme(
|
||||
_currentValue ? Themes.darkTheme : Themes.lightTheme);
|
||||
}));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final settingsStore = Provider.of<SettingsStore>(context);
|
||||
|
||||
return Container(
|
||||
color: Theme.of(context).accentTextTheme.title.backgroundColor,
|
||||
child: ListTile(
|
||||
contentPadding: EdgeInsets.only(left: 24.0, right: 24.0),
|
||||
title: Observer(
|
||||
builder: (_) => Text(settingsStore.itemHeaders[title],
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: Theme.of(context).primaryTextTheme.title.color)),
|
||||
),
|
||||
trailing: _getSwitch(context)),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:cake_wallet/src/widgets/standard_list.dart';
|
||||
import 'package:cake_wallet/src/widgets/standart_switch.dart';
|
||||
|
||||
class SettingsSwitcherCell extends StandardListRow {
|
||||
SettingsSwitcherCell(
|
||||
{@required String title, @required this.value, this.onValueChange})
|
||||
: super(title: title, isSelected: false);
|
||||
|
||||
final bool value;
|
||||
final void Function(bool value) onValueChange;
|
||||
|
||||
@override
|
||||
Widget buildTrailing(BuildContext context) =>
|
||||
StandartSwitch(value: value, onTaped: () => onValueChange(!value));
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:cake_wallet/src/stores/settings/settings_store.dart';
|
||||
|
||||
class SettingsTextListRow extends StatelessWidget {
|
||||
SettingsTextListRow({@required this.onTaped, this.title, this.widget});
|
||||
|
||||
final VoidCallback onTaped;
|
||||
final String title;
|
||||
final Widget widget;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final settingsStore = Provider.of<SettingsStore>(context);
|
||||
|
||||
return Container(
|
||||
color: Theme.of(context).accentTextTheme.title.backgroundColor,
|
||||
child: ListTile(
|
||||
contentPadding: EdgeInsets.only(left: 24.0, right: 24.0),
|
||||
title: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: <Widget>[
|
||||
Flexible(
|
||||
child: Observer(
|
||||
builder: (_) => Text(
|
||||
settingsStore.itemHeaders[title],
|
||||
style: TextStyle(
|
||||
fontSize: 14.0,
|
||||
color: Theme.of(context).primaryTextTheme.title.color),
|
||||
)),
|
||||
),
|
||||
Flexible(
|
||||
child: widget
|
||||
)
|
||||
],
|
||||
),
|
||||
onTap: onTaped,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/stores/wallet/wallet_keys_store.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/src/widgets/standart_list_row.dart';
|
||||
|
||||
class ShowKeysPage extends BasePage {
|
||||
@override
|
||||
String get title => S.current.wallet_keys;
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) {
|
||||
final walletKeysStore = Provider.of<WalletKeysStore>(context);
|
||||
|
||||
return Container(
|
||||
padding: EdgeInsets.only(top: 20.0, bottom: 20.0),
|
||||
child: Observer(
|
||||
builder: (_) {
|
||||
final keysMap = {
|
||||
S.of(context).view_key_public: walletKeysStore.publicViewKey,
|
||||
S.of(context).spend_key_private: walletKeysStore.privateSpendKey
|
||||
};
|
||||
|
||||
if (walletKeysStore.privateViewKey.isNotEmpty) {
|
||||
keysMap[S.of(context).view_key_private] =
|
||||
walletKeysStore.privateViewKey;
|
||||
}
|
||||
|
||||
if (walletKeysStore.publicSpendKey.isNotEmpty) {
|
||||
keysMap[S.of(context).spend_key_public] =
|
||||
walletKeysStore.publicSpendKey;
|
||||
}
|
||||
|
||||
return ListView.separated(
|
||||
separatorBuilder: (context, index) => Container(
|
||||
height: 1,
|
||||
padding: EdgeInsets.only(left: 24),
|
||||
color: Theme.of(context).accentTextTheme.title.backgroundColor,
|
||||
child: Container(
|
||||
height: 1,
|
||||
color: Theme.of(context).dividerColor,
|
||||
),
|
||||
),
|
||||
itemCount: keysMap.length,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
final key = keysMap.keys.elementAt(index);
|
||||
final value = keysMap.values.elementAt(index);
|
||||
|
||||
final isDrawTop = index == 0 ? true : false;
|
||||
final isDrawBottom = index == keysMap.length - 1 ? true : false;
|
||||
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
Clipboard.setData(ClipboardData(text: value));
|
||||
Scaffold.of(context).showSnackBar(SnackBar(
|
||||
content: Text(
|
||||
S.of(context).copied_key_to_clipboard(key),
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(color: Colors.white),
|
||||
),
|
||||
backgroundColor: Colors.green,
|
||||
duration: Duration(seconds: 1),
|
||||
));
|
||||
},
|
||||
child: StandartListRow(
|
||||
title: key + ':',
|
||||
value: value,
|
||||
isDrawTop: isDrawTop,
|
||||
isDrawBottom: isDrawBottom,
|
||||
),
|
||||
);
|
||||
});
|
||||
},
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -3,8 +3,8 @@ import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/view_model/address_list/address_edit_or_create_view_model.dart';
|
||||
import 'package:cake_wallet/core/AddressLabelValidator.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_edit_or_create_view_model.dart';
|
||||
import 'package:cake_wallet/core/address_label_validator.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
@@ -21,7 +21,7 @@ class AddressEditOrCreatePage extends BasePage {
|
||||
print(addressEditOrCreateViewModel.label);
|
||||
}
|
||||
|
||||
final AddressEditOrCreateViewModel addressEditOrCreateViewModel;
|
||||
final WalletAddressEditOrCreateViewModel addressEditOrCreateViewModel;
|
||||
final GlobalKey<FormState> _formKey;
|
||||
final TextEditingController _labelController;
|
||||
|
||||
|
||||
@@ -1,52 +1,21 @@
|
||||
import 'package:cake_wallet/src/domain/monero/monero_transaction_info.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/bitcoin/bitcoin_transaction_info.dart';
|
||||
import 'package:cake_wallet/src/domain/monero/monero_transaction_info.dart';
|
||||
import 'package:cake_wallet/src/domain/common/transaction_info.dart';
|
||||
import 'package:cake_wallet/src/stores/settings/settings_store.dart';
|
||||
import 'package:cake_wallet/src/screens/transaction_details/standart_list_item.dart';
|
||||
import 'package:cake_wallet/src/widgets/standart_list_row.dart';
|
||||
import 'package:cake_wallet/src/screens/transaction_details/standart_list_item.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/palette.dart';
|
||||
|
||||
class TransactionDetailsPage extends BasePage {
|
||||
TransactionDetailsPage({this.transactionInfo});
|
||||
|
||||
final TransactionInfo transactionInfo;
|
||||
|
||||
@override
|
||||
String get title => S.current.transaction_details_title;
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) {
|
||||
final settingsStore = Provider.of<SettingsStore>(context);
|
||||
|
||||
return TransactionDetailsForm(
|
||||
transactionInfo: transactionInfo, settingsStore: settingsStore);
|
||||
}
|
||||
}
|
||||
|
||||
class TransactionDetailsForm extends StatefulWidget {
|
||||
TransactionDetailsForm(
|
||||
{@required this.transactionInfo, @required this.settingsStore});
|
||||
|
||||
final TransactionInfo transactionInfo;
|
||||
final SettingsStore settingsStore;
|
||||
|
||||
@override
|
||||
TransactionDetailsFormState createState() => TransactionDetailsFormState();
|
||||
}
|
||||
|
||||
class TransactionDetailsFormState extends State<TransactionDetailsForm> {
|
||||
final _items = List<StandartListItem>();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
final _dateFormat = widget.settingsStore.getCurrentDateFormat(
|
||||
formatUSA: "yyyy.MM.dd, HH:mm", formatDefault: "dd.MM.yyyy, HH:mm");
|
||||
final tx = widget.transactionInfo;
|
||||
TransactionDetailsPage({this.transactionInfo}) : _items = [] {
|
||||
// FIXME
|
||||
// final _dateFormat = widget.settingsStore.getCurrentDateFormat(
|
||||
// formatUSA: "yyyy.MM.dd, HH:mm", formatDefault: "dd.MM.yyyy, HH:mm");
|
||||
final dateFormat = DateFormat('dd.MM.yyyy, HH:mm');
|
||||
final tx = transactionInfo;
|
||||
|
||||
if (tx is MoneroTransactionInfo) {
|
||||
final items = [
|
||||
@@ -54,7 +23,31 @@ class TransactionDetailsFormState extends State<TransactionDetailsForm> {
|
||||
title: S.current.transaction_details_transaction_id, value: tx.id),
|
||||
StandartListItem(
|
||||
title: S.current.transaction_details_date,
|
||||
value: _dateFormat.format(tx.date)),
|
||||
value: dateFormat.format(tx.date)),
|
||||
StandartListItem(
|
||||
title: S.current.transaction_details_height, value: '${tx.height}'),
|
||||
StandartListItem(
|
||||
title: S.current.transaction_details_amount,
|
||||
value: tx.amountFormatted())
|
||||
];
|
||||
// FIXME
|
||||
// if (widget.settingsStore.shouldSaveRecipientAddress &&
|
||||
// tx.recipientAddress != null) {
|
||||
// items.add(StandartListItem(
|
||||
// title: S.current.transaction_details_recipient_address,
|
||||
// value: tx.recipientAddress));
|
||||
// }
|
||||
|
||||
_items.addAll(items);
|
||||
}
|
||||
|
||||
if (tx is BitcoinTransactionInfo) {
|
||||
final items = [
|
||||
StandartListItem(
|
||||
title: S.current.transaction_details_transaction_id, value: tx.id),
|
||||
StandartListItem(
|
||||
title: S.current.transaction_details_date,
|
||||
value: dateFormat.format(tx.date)),
|
||||
StandartListItem(
|
||||
title: S.current.transaction_details_height, value: '${tx.height}'),
|
||||
StandartListItem(
|
||||
@@ -62,33 +55,31 @@ class TransactionDetailsFormState extends State<TransactionDetailsForm> {
|
||||
value: tx.amountFormatted())
|
||||
];
|
||||
|
||||
if (widget.settingsStore.shouldSaveRecipientAddress &&
|
||||
tx.recipientAddress != null) {
|
||||
items.add(StandartListItem(
|
||||
title: S.current.transaction_details_recipient_address,
|
||||
value: tx.recipientAddress));
|
||||
}
|
||||
|
||||
_items.addAll(items);
|
||||
}
|
||||
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
String get title => S.current.transaction_details_title;
|
||||
|
||||
final TransactionInfo transactionInfo;
|
||||
|
||||
final List<StandartListItem> _items;
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) {
|
||||
return Container(
|
||||
padding: EdgeInsets.only(top: 20, bottom: 20),
|
||||
child: ListView.separated(
|
||||
separatorBuilder: (context, index) => Container(
|
||||
height: 1,
|
||||
padding: EdgeInsets.only(left: 24),
|
||||
color: Theme.of(context).accentTextTheme.title.backgroundColor,
|
||||
child: Container(
|
||||
height: 1,
|
||||
color: Theme.of(context).dividerColor,
|
||||
),
|
||||
),
|
||||
height: 1,
|
||||
padding: EdgeInsets.only(left: 24),
|
||||
color: Theme.of(context).accentTextTheme.title.backgroundColor,
|
||||
child: Container(
|
||||
height: 1,
|
||||
color: Theme.of(context).dividerColor,
|
||||
),
|
||||
),
|
||||
itemCount: _items.length,
|
||||
itemBuilder: (context, index) {
|
||||
final item = _items[index];
|
||||
@@ -108,8 +99,7 @@ class TransactionDetailsFormState extends State<TransactionDetailsForm> {
|
||||
),
|
||||
);
|
||||
},
|
||||
child:
|
||||
StandartListRow(
|
||||
child: StandartListRow(
|
||||
title: '${item.title}:',
|
||||
value: item.value,
|
||||
isDrawTop: isDrawTop,
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/src/widgets/standart_list_row.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_keys_view_model.dart';
|
||||
|
||||
class WalletKeysPage extends BasePage {
|
||||
WalletKeysPage(this.walletKeysViewModel);
|
||||
|
||||
@override
|
||||
String get title => S.current.wallet_keys;
|
||||
|
||||
final WalletKeysViewModel walletKeysViewModel;
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) {
|
||||
return Container(
|
||||
padding: EdgeInsets.only(top: 20.0, bottom: 20.0),
|
||||
child: Observer(
|
||||
builder: (_) {
|
||||
return ListView.separated(
|
||||
separatorBuilder: (context, index) => Container(
|
||||
height: 1,
|
||||
padding: EdgeInsets.only(left: 24),
|
||||
color: Theme.of(context)
|
||||
.accentTextTheme
|
||||
.title
|
||||
.backgroundColor,
|
||||
child: Container(
|
||||
height: 1,
|
||||
color: Theme.of(context).dividerColor,
|
||||
),
|
||||
),
|
||||
itemCount: walletKeysViewModel.items.length,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
final item = walletKeysViewModel.items[index];
|
||||
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
Clipboard.setData(ClipboardData(text: item.value));
|
||||
Scaffold.of(context).showSnackBar(SnackBar(
|
||||
content: Text(
|
||||
S.of(context).copied_key_to_clipboard(item.title),
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(color: Colors.white),
|
||||
),
|
||||
backgroundColor: Colors.green,
|
||||
duration: Duration(seconds: 1),
|
||||
));
|
||||
},
|
||||
child: StandartListRow(
|
||||
title: item.title + ':',
|
||||
value: item.value,
|
||||
),
|
||||
);
|
||||
});
|
||||
},
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -1,177 +1,188 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
import 'package:cake_wallet/palette.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/domain/common/wallet_type.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_list/wallet_list_view_model.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
|
||||
import 'package:cake_wallet/src/stores/wallet_list/wallet_list_store.dart';
|
||||
import 'package:cake_wallet/src/stores/wallet/wallet_store.dart';
|
||||
import 'package:cake_wallet/src/screens/wallet_list/wallet_menu.dart';
|
||||
import 'package:cake_wallet/src/screens/wallet_list/widgets/wallet_tile.dart';
|
||||
|
||||
class WalletListPage extends BasePage {
|
||||
WalletListPage({this.walletListViewModel});
|
||||
|
||||
final WalletListViewModel walletListViewModel;
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) => WalletListBody();
|
||||
Widget body(BuildContext context) =>
|
||||
WalletListBody(walletListViewModel: walletListViewModel);
|
||||
}
|
||||
|
||||
class WalletListBody extends StatefulWidget {
|
||||
WalletListBody({this.walletListViewModel});
|
||||
|
||||
final WalletListViewModel walletListViewModel;
|
||||
|
||||
@override
|
||||
WalletListBodyState createState() => WalletListBodyState();
|
||||
}
|
||||
|
||||
class WalletListBodyState extends State<WalletListBody> {
|
||||
final moneroIcon = Image.asset('assets/images/monero.png', height: 24, width: 24);
|
||||
WalletListStore _walletListStore;
|
||||
ScrollController scrollController = ScrollController();
|
||||
final moneroIcon =
|
||||
Image.asset('assets/images/monero.png', height: 24, width: 24);
|
||||
final bitcoinIcon =
|
||||
Image.asset('assets/images/bitcoin.png', height: 24, width: 24);
|
||||
final scrollController = ScrollController();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final walletStore = Provider.of<WalletStore>(context);
|
||||
_walletListStore = Provider.of<WalletListStore>(context);
|
||||
|
||||
final newWalletImage = Image.asset('assets/images/new_wallet.png',
|
||||
height: 12,
|
||||
width: 12,
|
||||
color: Palette.oceanBlue);
|
||||
height: 12, width: 12, color: Palette.oceanBlue);
|
||||
final restoreWalletImage = Image.asset('assets/images/restore_wallet.png',
|
||||
height: 12,
|
||||
width: 12,
|
||||
color: Theme.of(context).primaryTextTheme.title.color);
|
||||
|
||||
return SafeArea(
|
||||
child: Container(
|
||||
padding: EdgeInsets.only(top: 16),
|
||||
child: ScrollableWithBottomSection(
|
||||
contentPadding: EdgeInsets.only(bottom: 20),
|
||||
content: Container(
|
||||
child: Observer(
|
||||
builder: (_) => ListView.separated(
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
separatorBuilder: (_, index) => Divider(
|
||||
color: Theme.of(context).backgroundColor,
|
||||
height: 16),
|
||||
itemCount: _walletListStore.wallets.length,
|
||||
itemBuilder: (__, index) {
|
||||
final wallet = _walletListStore.wallets[index];
|
||||
final screenWidth = MediaQuery.of(context).size.width;
|
||||
child: Container(
|
||||
padding: EdgeInsets.only(top: 16),
|
||||
child: ScrollableWithBottomSection(
|
||||
contentPadding: EdgeInsets.only(bottom: 20),
|
||||
content: Container(
|
||||
child: Observer(
|
||||
builder: (_) => ListView.separated(
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
separatorBuilder: (_, index) => Divider(
|
||||
color: Theme.of(context).backgroundColor, height: 16),
|
||||
itemCount: widget.walletListViewModel.wallets.length,
|
||||
itemBuilder: (__, index) {
|
||||
final wallet = widget.walletListViewModel.wallets[index];
|
||||
final screenWidth = MediaQuery.of(context).size.width;
|
||||
// String shortAddress = '';
|
||||
|
||||
final isCurrentWallet =
|
||||
_walletListStore.isCurrentWallet(wallet);
|
||||
// if (wallet.isCurrent) {
|
||||
// shortAddress = wallet.address;
|
||||
// shortAddress = shortAddress.replaceRange(
|
||||
// 4, shortAddress.length - 4, '...');
|
||||
// }
|
||||
|
||||
String shortAddress = '';
|
||||
final walletMenu = WalletMenu(context, widget.walletListViewModel);
|
||||
final items =
|
||||
walletMenu.generateItemsForWalletMenu(wallet.isCurrent);
|
||||
final colors = walletMenu
|
||||
.generateColorsForWalletMenu(wallet.isCurrent);
|
||||
final images = walletMenu
|
||||
.generateImagesForWalletMenu(wallet.isCurrent);
|
||||
|
||||
if (isCurrentWallet) {
|
||||
shortAddress = walletStore.subaddress.address;
|
||||
shortAddress = shortAddress.replaceRange(4, shortAddress.length - 4, '...');
|
||||
}
|
||||
return Container(
|
||||
height: 108,
|
||||
width: double.infinity,
|
||||
child: CustomScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
controller: scrollController,
|
||||
slivers: <Widget>[
|
||||
SliverPersistentHeader(
|
||||
pinned: false,
|
||||
floating: true,
|
||||
delegate: WalletTile(
|
||||
min: screenWidth - 228,
|
||||
max: screenWidth,
|
||||
image: _imageFor(type: wallet.type),
|
||||
walletName: wallet.name,
|
||||
walletAddress: '', //shortAddress,
|
||||
isCurrent: wallet.isCurrent),
|
||||
),
|
||||
SliverList(
|
||||
delegate:
|
||||
SliverChildBuilderDelegate((context, index) {
|
||||
final item = items[index];
|
||||
final color = colors[index];
|
||||
final image = images[index];
|
||||
|
||||
final walletMenu = WalletMenu(context);
|
||||
final items = walletMenu.generateItemsForWalletMenu(isCurrentWallet);
|
||||
final colors = walletMenu.generateColorsForWalletMenu(isCurrentWallet);
|
||||
final images = walletMenu.generateImagesForWalletMenu(isCurrentWallet);
|
||||
final radius = index == 0 ? 12.0 : 0.0;
|
||||
|
||||
return Container(
|
||||
height: 108,
|
||||
width: double.infinity,
|
||||
child: CustomScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
controller: scrollController,
|
||||
slivers: <Widget>[
|
||||
SliverPersistentHeader(
|
||||
pinned: false,
|
||||
floating: true,
|
||||
delegate: WalletTile(
|
||||
min: screenWidth - 228,
|
||||
max: screenWidth,
|
||||
image: moneroIcon,
|
||||
walletName: wallet.name,
|
||||
walletAddress: shortAddress,
|
||||
isCurrent: isCurrentWallet
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
scrollController.animateTo(0.0,
|
||||
duration: Duration(milliseconds: 500),
|
||||
curve: Curves.fastOutSlowIn);
|
||||
walletMenu.action(
|
||||
walletMenu.listItems.indexOf(item),
|
||||
wallet,
|
||||
wallet.isCurrent);
|
||||
},
|
||||
child: Container(
|
||||
height: 108,
|
||||
width: 108,
|
||||
color: Theme.of(context).backgroundColor,
|
||||
child: Container(
|
||||
padding: EdgeInsets.only(left: 5, right: 5),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(radius),
|
||||
bottomLeft: Radius.circular(radius)),
|
||||
color: color),
|
||||
child: Center(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
image,
|
||||
SizedBox(height: 5),
|
||||
Text(
|
||||
item,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: Colors.white),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
SliverList(
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(context, index) {
|
||||
|
||||
final item = items[index];
|
||||
final color = colors[index];
|
||||
final image = images[index];
|
||||
|
||||
final radius = index == 0 ? 12.0 : 0.0;
|
||||
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
scrollController.animateTo(0.0, duration: Duration(milliseconds: 500), curve: Curves.fastOutSlowIn);
|
||||
walletMenu.action(
|
||||
walletMenu.listItems.indexOf(item), wallet, isCurrentWallet);
|
||||
},
|
||||
child: Container(
|
||||
height: 108,
|
||||
width: 108,
|
||||
color: Theme.of(context).backgroundColor,
|
||||
child: Container(
|
||||
padding: EdgeInsets.only(left: 5, right: 5),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(radius),
|
||||
bottomLeft: Radius.circular(radius)
|
||||
),
|
||||
color: color
|
||||
),
|
||||
child: Center(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
image,
|
||||
SizedBox(height: 5),
|
||||
Text(
|
||||
item,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: Colors.white
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
childCount: items.length
|
||||
)
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
);
|
||||
}, childCount: items.length))
|
||||
],
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
bottomSection: Column(children: <Widget>[
|
||||
PrimaryImageButton(
|
||||
onPressed: () => Navigator.of(context).pushNamed(Routes.newWalletType),
|
||||
image: newWalletImage,
|
||||
text: S.of(context).wallet_list_create_new_wallet,
|
||||
color: Colors.white,
|
||||
textColor: Palette.oceanBlue,
|
||||
borderColor: Palette.oceanBlue,
|
||||
),
|
||||
SizedBox(height: 10.0),
|
||||
PrimaryImageButton(
|
||||
onPressed: () =>
|
||||
Navigator.of(context).pushNamed(Routes.restoreWalletOptions),
|
||||
),
|
||||
bottomSection: Column(children: <Widget>[
|
||||
PrimaryImageButton(
|
||||
onPressed: () =>
|
||||
Navigator.of(context).pushNamed(Routes.newWalletType),
|
||||
image: newWalletImage,
|
||||
text: S.of(context).wallet_list_create_new_wallet,
|
||||
color: Colors.white,
|
||||
textColor: Palette.oceanBlue,
|
||||
borderColor: Palette.oceanBlue,
|
||||
),
|
||||
SizedBox(height: 10.0),
|
||||
PrimaryImageButton(
|
||||
onPressed: () => Navigator.of(context)
|
||||
.pushNamed(Routes.restoreWalletType),
|
||||
image: restoreWalletImage,
|
||||
text: S.of(context).wallet_list_restore_wallet,
|
||||
color: Theme.of(context).primaryTextTheme.overline.color,
|
||||
textColor: Theme.of(context).primaryTextTheme.title.color)
|
||||
])),
|
||||
)
|
||||
);
|
||||
])),
|
||||
));
|
||||
}
|
||||
|
||||
Image _imageFor({WalletType type}) {
|
||||
switch (type) {
|
||||
case WalletType.bitcoin:
|
||||
return bitcoinIcon;
|
||||
case WalletType.monero:
|
||||
return moneroIcon;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
import 'package:cake_wallet/view_model/wallet_list/wallet_list_view_model.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/stores/wallet_list/wallet_list_store.dart';
|
||||
import 'package:cake_wallet/src/domain/common/wallet_description.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_list/wallet_list_item.dart';
|
||||
import 'package:cake_wallet/src/screens/auth/auth_page.dart';
|
||||
|
||||
class WalletMenu {
|
||||
WalletMenu(this.context);
|
||||
WalletMenu(this.context, this.walletListViewModel);
|
||||
|
||||
final WalletListViewModel walletListViewModel;
|
||||
final BuildContext context;
|
||||
|
||||
final List<String> listItems = [
|
||||
@@ -65,9 +67,7 @@ class WalletMenu {
|
||||
return images;
|
||||
}
|
||||
|
||||
void action(int index, WalletDescription wallet, bool isCurrentWallet) {
|
||||
final _walletListStore = Provider.of<WalletListStore>(context);
|
||||
|
||||
void action(int index, WalletListItem wallet, bool isCurrentWallet) {
|
||||
switch (index) {
|
||||
case 0:
|
||||
Navigator.of(context).pushNamed(Routes.auth, arguments:
|
||||
@@ -79,7 +79,7 @@ class WalletMenu {
|
||||
try {
|
||||
auth.changeProcessText(
|
||||
S.of(context).wallet_list_loading_wallet(wallet.name));
|
||||
await _walletListStore.loadWallet(wallet);
|
||||
await walletListViewModel.loadWallet(wallet);
|
||||
auth.close();
|
||||
Navigator.of(context).pop();
|
||||
} catch (e) {
|
||||
@@ -109,7 +109,7 @@ class WalletMenu {
|
||||
try {
|
||||
auth.changeProcessText(
|
||||
S.of(context).wallet_list_removing_wallet(wallet.name));
|
||||
await _walletListStore.remove(wallet);
|
||||
// await _walletListStore.remove(wallet);
|
||||
auth.close();
|
||||
} catch (e) {
|
||||
auth.changeProcessText(S
|
||||
|
||||
@@ -20,11 +20,10 @@ class WalletTile extends SliverPersistentHeaderDelegate {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) {
|
||||
|
||||
double opacity = 1 - shrinkOffset / (max - min);
|
||||
var opacity = 1 - shrinkOffset / (max - min);
|
||||
opacity = opacity >= 0 ? opacity : 0;
|
||||
|
||||
double panelWidth = 12 * opacity;
|
||||
var panelWidth = 12 * opacity;
|
||||
panelWidth = panelWidth < 12 ? 0 : 12;
|
||||
|
||||
final currentColor = isCurrent
|
||||
|
||||
@@ -4,11 +4,14 @@ import 'package:cake_wallet/routes.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:cake_wallet/themes.dart';
|
||||
import 'package:cake_wallet/theme_changer.dart';
|
||||
|
||||
class WelcomePage extends BasePage {
|
||||
static const aspectRatioImage = 1.25;
|
||||
final welcomeImage = Image.asset('assets/images/welcome.png');
|
||||
final welcomeImageLight = Image.asset('assets/images/welcome_light.png');
|
||||
final welcomeImageDark = Image.asset('assets/images/welcome.png');
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -20,6 +23,10 @@ class WelcomePage extends BasePage {
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) {
|
||||
final _themeChanger = Provider.of<ThemeChanger>(context);
|
||||
final welcomeImage = _themeChanger.getTheme() == Themes.darkTheme
|
||||
? welcomeImageDark : welcomeImageLight;
|
||||
|
||||
final newWalletImage = Image.asset('assets/images/new_wallet.png',
|
||||
height: 12,
|
||||
width: 12,
|
||||
@@ -30,89 +37,97 @@ class WelcomePage extends BasePage {
|
||||
color: Theme.of(context).primaryTextTheme.title.color);
|
||||
|
||||
return Container(
|
||||
padding: EdgeInsets.only(top: 20),
|
||||
child: ScrollableWithBottomSection(
|
||||
contentPadding: EdgeInsets.only(bottom: 20),
|
||||
content: Column(
|
||||
children: <Widget>[
|
||||
AspectRatio(
|
||||
padding: EdgeInsets.all(24),
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
Flexible(
|
||||
flex: 2,
|
||||
child: AspectRatio(
|
||||
aspectRatio: aspectRatioImage,
|
||||
child: FittedBox(child: welcomeImage, fit: BoxFit.fill)),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(left: 24, right: 24, top: 40),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
S.of(context).welcome,
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
color: Theme.of(context).primaryTextTheme.caption.color,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 10),
|
||||
child: Text(
|
||||
S.of(context).cake_wallet,
|
||||
style: TextStyle(
|
||||
fontSize: 36,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Theme.of(context).primaryTextTheme.title.color,
|
||||
child: FittedBox(child: welcomeImage, fit: BoxFit.fill)
|
||||
)
|
||||
),
|
||||
Flexible(
|
||||
flex: 3,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: <Widget>[
|
||||
Column(
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 24),
|
||||
child: Text(
|
||||
S.of(context).welcome,
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
color: Theme.of(context).primaryTextTheme.caption.color,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 14),
|
||||
child: Text(
|
||||
S.of(context).first_wallet_text,
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 10),
|
||||
child: Text(
|
||||
S.of(context).cake_wallet,
|
||||
style: TextStyle(
|
||||
fontSize: 36,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Theme.of(context).primaryTextTheme.title.color,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 14),
|
||||
child: Text(
|
||||
S.of(context).first_wallet_text,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Theme.of(context).primaryTextTheme.caption.color,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Column(
|
||||
children: <Widget>[
|
||||
Text(
|
||||
S.of(context).please_make_selection,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 12,
|
||||
color: Theme.of(context).primaryTextTheme.caption.color,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 24),
|
||||
child: PrimaryImageButton(
|
||||
onPressed: () => Navigator.pushNamed(context, Routes.newWalletFromWelcome),
|
||||
image: newWalletImage,
|
||||
text: S.of(context).create_new,
|
||||
color: Colors.white,
|
||||
textColor: Palette.oceanBlue,
|
||||
borderColor: Palette.oceanBlue,
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 10),
|
||||
child: PrimaryImageButton(
|
||||
onPressed: () => Navigator.pushNamed(context, Routes.restoreOptions),
|
||||
image: restoreWalletImage,
|
||||
text: S.of(context).restore_wallet,
|
||||
color: Theme.of(context).primaryTextTheme.overline.color,
|
||||
textColor: Theme.of(context).primaryTextTheme.title.color),
|
||||
)
|
||||
],
|
||||
)
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
bottomSectionPadding: EdgeInsets.only(left: 24, right: 24, bottom: 20),
|
||||
bottomSection: Column(children: <Widget>[
|
||||
Text(
|
||||
S.of(context).please_make_selection,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: Theme.of(context).primaryTextTheme.caption.color,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 24),
|
||||
child: PrimaryImageButton(
|
||||
onPressed: () => Navigator.pushNamed(context, Routes.newWalletFromWelcome),
|
||||
image: newWalletImage,
|
||||
text: S.of(context).create_new,
|
||||
color: Colors.white,
|
||||
textColor: Palette.oceanBlue,
|
||||
borderColor: Palette.oceanBlue,
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 10),
|
||||
child: PrimaryImageButton(
|
||||
onPressed: () => Navigator.pushNamed(context, Routes.restoreOptions),
|
||||
image: restoreWalletImage,
|
||||
text: S.of(context).restore_wallet,
|
||||
color: Theme.of(context).primaryTextTheme.overline.color,
|
||||
textColor: Theme.of(context).primaryTextTheme.title.color),
|
||||
)
|
||||
]),
|
||||
),
|
||||
],
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,111 +1,111 @@
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/domain/common/contact.dart';
|
||||
import 'package:cake_wallet/src/domain/common/crypto_currency.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
|
||||
part 'address_book_store.g.dart';
|
||||
|
||||
class AddressBookStore = AddressBookStoreBase with _$AddressBookStore;
|
||||
|
||||
abstract class AddressBookStoreBase with Store {
|
||||
AddressBookStoreBase({@required this.contacts}) {
|
||||
updateContactList();
|
||||
isDisabledStatus = true;
|
||||
}
|
||||
|
||||
@observable
|
||||
List<Contact> contactList;
|
||||
|
||||
@observable
|
||||
bool isDisabledStatus;
|
||||
|
||||
@observable
|
||||
bool isValid;
|
||||
|
||||
@observable
|
||||
String errorMessage;
|
||||
|
||||
Box<Contact> contacts;
|
||||
|
||||
@action
|
||||
Future add({Contact contact}) async => contacts.add(contact);
|
||||
|
||||
@action
|
||||
Future updateContactList() async => contactList = contacts.values.toList();
|
||||
|
||||
@action
|
||||
Future update({Contact contact}) async => contact.save();
|
||||
|
||||
@action
|
||||
Future delete({Contact contact}) async => await contact.delete();
|
||||
|
||||
@action
|
||||
void setDisabledStatus(bool isDisabled) {
|
||||
isDisabledStatus = isDisabled;
|
||||
}
|
||||
|
||||
void validateContactName(String value) {
|
||||
const pattern = '''^[^`,'"]{1,32}\$''';
|
||||
final regExp = RegExp(pattern);
|
||||
isValid = regExp.hasMatch(value);
|
||||
errorMessage = isValid ? null : S.current.error_text_contact_name;
|
||||
}
|
||||
|
||||
void validateAddress(String value, {CryptoCurrency cryptoCurrency}) {
|
||||
// XMR (95, 106), ADA (59, 92, 105), BCH (42), BNB (42), BTC (34, 42), DASH (34), EOS (42),
|
||||
// ETH (42), LTC (34), NANO (64, 65), TRX (34), USDT (42), XLM (56), XRP (34)
|
||||
const pattern = '^[0-9a-zA-Z]{95}\$|^[0-9a-zA-Z]{34}\$|^[0-9a-zA-Z]{42}\$|^[0-9a-zA-Z]{56}\$|^[0-9a-zA-Z]{59}\$|^[0-9a-zA-Z_]{64}\$|^[0-9a-zA-Z_]{65}\$|^[0-9a-zA-Z]{92}\$|^[0-9a-zA-Z]{105}\$|^[0-9a-zA-Z]{106}\$';
|
||||
final regExp = RegExp(pattern);
|
||||
isValid = regExp.hasMatch(value);
|
||||
if (isValid && cryptoCurrency != null) {
|
||||
switch (cryptoCurrency) {
|
||||
case CryptoCurrency.xmr:
|
||||
isValid = (value.length == 95)||(value.length == 106);
|
||||
break;
|
||||
case CryptoCurrency.ada:
|
||||
isValid = (value.length == 59)||(value.length == 92)||(value.length == 105);
|
||||
break;
|
||||
case CryptoCurrency.bch:
|
||||
isValid = (value.length == 42);
|
||||
break;
|
||||
case CryptoCurrency.bnb:
|
||||
isValid = (value.length == 42);
|
||||
break;
|
||||
case CryptoCurrency.btc:
|
||||
isValid = (value.length == 34)||(value.length == 42);
|
||||
break;
|
||||
case CryptoCurrency.dash:
|
||||
isValid = (value.length == 34);
|
||||
break;
|
||||
case CryptoCurrency.eos:
|
||||
isValid = (value.length == 42);
|
||||
break;
|
||||
case CryptoCurrency.eth:
|
||||
isValid = (value.length == 42);
|
||||
break;
|
||||
case CryptoCurrency.ltc:
|
||||
isValid = (value.length == 34);
|
||||
break;
|
||||
case CryptoCurrency.nano:
|
||||
isValid = (value.length == 64)||(value.length == 65);
|
||||
break;
|
||||
case CryptoCurrency.trx:
|
||||
isValid = (value.length == 34);
|
||||
break;
|
||||
case CryptoCurrency.usdt:
|
||||
isValid = (value.length == 42);
|
||||
break;
|
||||
case CryptoCurrency.xlm:
|
||||
isValid = (value.length == 56);
|
||||
break;
|
||||
case CryptoCurrency.xrp:
|
||||
isValid = (value.length == 34);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
errorMessage = isValid ? null : S.current.error_text_address;
|
||||
}
|
||||
}
|
||||
//import 'package:mobx/mobx.dart';
|
||||
//import 'package:flutter/foundation.dart';
|
||||
//import 'package:cake_wallet/generated/i18n.dart';
|
||||
//import 'package:cake_wallet/src/domain/common/contact.dart';
|
||||
//import 'package:cake_wallet/src/domain/common/crypto_currency.dart';
|
||||
//import 'package:hive/hive.dart';
|
||||
//
|
||||
//part 'address_book_store.g.dart';
|
||||
//
|
||||
//class AddressBookStore = AddressBookStoreBase with _$AddressBookStore;
|
||||
//
|
||||
//abstract class AddressBookStoreBase with Store {
|
||||
// AddressBookStoreBase({@required this.contacts}) {
|
||||
// updateContactList();
|
||||
// isDisabledStatus = true;
|
||||
// }
|
||||
//
|
||||
// @observable
|
||||
// List<Contact> contactList;
|
||||
//
|
||||
// @observable
|
||||
// bool isDisabledStatus;
|
||||
//
|
||||
// @observable
|
||||
// bool isValid;
|
||||
//
|
||||
// @observable
|
||||
// String errorMessage;
|
||||
//
|
||||
// Box<Contact> contacts;
|
||||
//
|
||||
// @action
|
||||
// Future add({Contact contact}) async => contacts.add(contact);
|
||||
//
|
||||
// @action
|
||||
// Future updateContactList() async => contactList = contacts.values.toList();
|
||||
//
|
||||
// @action
|
||||
// Future update({Contact contact}) async => contact.save();
|
||||
//
|
||||
// @action
|
||||
// Future delete({Contact contact}) async => await contact.delete();
|
||||
//
|
||||
// @action
|
||||
// void setDisabledStatus(bool isDisabled) {
|
||||
// isDisabledStatus = isDisabled;
|
||||
// }
|
||||
//
|
||||
// void validateContactName(String value) {
|
||||
// const pattern = '''^[^`,'"]{1,32}\$''';
|
||||
// final regExp = RegExp(pattern);
|
||||
// isValid = regExp.hasMatch(value);
|
||||
// errorMessage = isValid ? null : S.current.error_text_contact_name;
|
||||
// }
|
||||
//
|
||||
// void validateAddress(String value, {CryptoCurrency cryptoCurrency}) {
|
||||
// // XMR (95, 106), ADA (59, 92, 105), BCH (42), BNB (42), BTC (34, 42), DASH (34), EOS (42),
|
||||
// // ETH (42), LTC (34), NANO (64, 65), TRX (34), USDT (42), XLM (56), XRP (34)
|
||||
// const pattern = '^[0-9a-zA-Z]{95}\$|^[0-9a-zA-Z]{34}\$|^[0-9a-zA-Z]{42}\$|^[0-9a-zA-Z]{56}\$|^[0-9a-zA-Z]{59}\$|^[0-9a-zA-Z_]{64}\$|^[0-9a-zA-Z_]{65}\$|^[0-9a-zA-Z]{92}\$|^[0-9a-zA-Z]{105}\$|^[0-9a-zA-Z]{106}\$';
|
||||
// final regExp = RegExp(pattern);
|
||||
// isValid = regExp.hasMatch(value);
|
||||
// if (isValid && cryptoCurrency != null) {
|
||||
// switch (cryptoCurrency) {
|
||||
// case CryptoCurrency.xmr:
|
||||
// isValid = (value.length == 95)||(value.length == 106);
|
||||
// break;
|
||||
// case CryptoCurrency.ada:
|
||||
// isValid = (value.length == 59)||(value.length == 92)||(value.length == 105);
|
||||
// break;
|
||||
// case CryptoCurrency.bch:
|
||||
// isValid = (value.length == 42);
|
||||
// break;
|
||||
// case CryptoCurrency.bnb:
|
||||
// isValid = (value.length == 42);
|
||||
// break;
|
||||
// case CryptoCurrency.btc:
|
||||
// isValid = (value.length == 34)||(value.length == 42);
|
||||
// break;
|
||||
// case CryptoCurrency.dash:
|
||||
// isValid = (value.length == 34);
|
||||
// break;
|
||||
// case CryptoCurrency.eos:
|
||||
// isValid = (value.length == 42);
|
||||
// break;
|
||||
// case CryptoCurrency.eth:
|
||||
// isValid = (value.length == 42);
|
||||
// break;
|
||||
// case CryptoCurrency.ltc:
|
||||
// isValid = (value.length == 34);
|
||||
// break;
|
||||
// case CryptoCurrency.nano:
|
||||
// isValid = (value.length == 64)||(value.length == 65);
|
||||
// break;
|
||||
// case CryptoCurrency.trx:
|
||||
// isValid = (value.length == 34);
|
||||
// break;
|
||||
// case CryptoCurrency.usdt:
|
||||
// isValid = (value.length == 42);
|
||||
// break;
|
||||
// case CryptoCurrency.xlm:
|
||||
// isValid = (value.length == 56);
|
||||
// break;
|
||||
// case CryptoCurrency.xrp:
|
||||
// isValid = (value.length == 34);
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// errorMessage = isValid ? null : S.current.error_text_address;
|
||||
// }
|
||||
//}
|
||||
|
||||
@@ -33,10 +33,8 @@ abstract class ExchangeStoreBase with Store {
|
||||
provider = initialProvider;
|
||||
depositCurrency = initialDepositCurrency;
|
||||
receiveCurrency = initialReceiveCurrency;
|
||||
/*isDepositAddressEnabled = !(depositCurrency == walletStore.type);
|
||||
isReceiveAddressEnabled = !(receiveCurrency == walletStore.type);*/
|
||||
isDepositAddressEnabled = true;
|
||||
isReceiveAddressEnabled = true;
|
||||
isDepositAddressEnabled = !(depositCurrency == walletStore.type);
|
||||
isReceiveAddressEnabled = !(receiveCurrency == walletStore.type);
|
||||
depositAmount = '';
|
||||
receiveAmount = '';
|
||||
depositAddress = '';
|
||||
@@ -109,16 +107,16 @@ abstract class ExchangeStoreBase with Store {
|
||||
void changeDepositCurrency({CryptoCurrency currency}) {
|
||||
depositCurrency = currency;
|
||||
_onPairChange();
|
||||
/*isDepositAddressEnabled = !(depositCurrency == walletStore.type);
|
||||
isReceiveAddressEnabled = !(receiveCurrency == walletStore.type);*/
|
||||
isDepositAddressEnabled = !(depositCurrency == walletStore.type);
|
||||
isReceiveAddressEnabled = !(receiveCurrency == walletStore.type);
|
||||
}
|
||||
|
||||
@action
|
||||
void changeReceiveCurrency({CryptoCurrency currency}) {
|
||||
receiveCurrency = currency;
|
||||
_onPairChange();
|
||||
/*isDepositAddressEnabled = !(depositCurrency == walletStore.type);
|
||||
isReceiveAddressEnabled = !(receiveCurrency == walletStore.type);*/
|
||||
isDepositAddressEnabled = !(depositCurrency == walletStore.type);
|
||||
isReceiveAddressEnabled = !(receiveCurrency == walletStore.type);
|
||||
}
|
||||
|
||||
@action
|
||||
@@ -242,8 +240,8 @@ abstract class ExchangeStoreBase with Store {
|
||||
receiveCurrency = CryptoCurrency.btc;
|
||||
depositAddress = depositCurrency == walletStore.type ? walletStore.address : '';
|
||||
receiveAddress = receiveCurrency == walletStore.type ? walletStore.address : '';
|
||||
/*isDepositAddressEnabled = !(depositCurrency == walletStore.type);
|
||||
isReceiveAddressEnabled = !(receiveCurrency == walletStore.type);*/
|
||||
isDepositAddressEnabled = !(depositCurrency == walletStore.type);
|
||||
isReceiveAddressEnabled = !(receiveCurrency == walletStore.type);
|
||||
_onPairChange();
|
||||
}
|
||||
|
||||
|
||||
@@ -72,8 +72,7 @@ abstract class NodeListBase with Store {
|
||||
|
||||
Future<bool> isNodeOnline(Node node) async {
|
||||
try {
|
||||
return await node.requestNode(node.uri,
|
||||
login: node.login, password: node.password);
|
||||
return await node.requestNode();
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -38,103 +38,115 @@ class AddressTextField extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return TextFormField(
|
||||
onFieldSubmitted: (_) => FocusScope.of(context).unfocus(),
|
||||
enabled: isActive,
|
||||
controller: controller,
|
||||
focusNode: focusNode,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
color: Theme.of(context).primaryTextTheme.title.color
|
||||
),
|
||||
decoration: InputDecoration(
|
||||
suffixIcon: SizedBox(
|
||||
width: prefixIconWidth * options.length +
|
||||
(spaceBetweenPrefixIcons * options.length),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
SizedBox(width: 5),
|
||||
if (this.options.contains(AddressTextFieldOption.qrCode)) ...[
|
||||
Container(
|
||||
width: prefixIconWidth,
|
||||
height: prefixIconHeight,
|
||||
padding: EdgeInsets.only(top: 0),
|
||||
child: InkWell(
|
||||
onTap: () async => _presentQRScanner(context),
|
||||
child: Container(
|
||||
padding: EdgeInsets.all(8),
|
||||
decoration: BoxDecoration(
|
||||
color: buttonColor ?? Theme.of(context).accentTextTheme.title.color,
|
||||
borderRadius:
|
||||
BorderRadius.all(Radius.circular(6))),
|
||||
child: Image.asset('assets/images/qr_code_icon.png')),
|
||||
))
|
||||
],
|
||||
if (this
|
||||
.options
|
||||
.contains(AddressTextFieldOption.addressBook)) ...[
|
||||
Container(
|
||||
width: prefixIconWidth,
|
||||
height: prefixIconHeight,
|
||||
padding: EdgeInsets.only(top: 0),
|
||||
child: InkWell(
|
||||
onTap: () async => _presetAddressBookPicker(context),
|
||||
child: Container(
|
||||
padding: EdgeInsets.all(8),
|
||||
decoration: BoxDecoration(
|
||||
color: buttonColor ?? Theme.of(context).accentTextTheme.title.color,
|
||||
borderRadius:
|
||||
BorderRadius.all(Radius.circular(6))),
|
||||
child: Image.asset(
|
||||
'assets/images/open_book.png')),
|
||||
))
|
||||
],
|
||||
if (this
|
||||
.options
|
||||
.contains(AddressTextFieldOption.subaddressList)) ...[
|
||||
Container(
|
||||
width: prefixIconWidth,
|
||||
height: prefixIconHeight,
|
||||
padding: EdgeInsets.only(top: 0),
|
||||
child: InkWell(
|
||||
onTap: () async => _presetSubaddressListPicker(context),
|
||||
child: Container(
|
||||
padding: EdgeInsets.all(8),
|
||||
decoration: BoxDecoration(
|
||||
color: buttonColor ?? Theme.of(context).accentTextTheme.title.color,
|
||||
borderRadius:
|
||||
BorderRadius.all(Radius.circular(6))),
|
||||
child: Image.asset(
|
||||
'assets/images/receive_icon_raw.png')),
|
||||
))
|
||||
],
|
||||
],
|
||||
return Stack(
|
||||
children: <Widget>[
|
||||
TextFormField(
|
||||
onFieldSubmitted: (_) => FocusScope.of(context).unfocus(),
|
||||
enabled: isActive,
|
||||
controller: controller,
|
||||
focusNode: focusNode,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
color: Theme.of(context).primaryTextTheme.title.color
|
||||
),
|
||||
),
|
||||
hintStyle: TextStyle(
|
||||
fontSize: 16,
|
||||
color: Theme.of(context).primaryTextTheme.caption.color
|
||||
),
|
||||
hintText: placeholder ?? S.current.widgets_address,
|
||||
focusedBorder: isBorderExist
|
||||
? UnderlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).dividerColor,
|
||||
width: 1.0))
|
||||
: InputBorder.none,
|
||||
disabledBorder: isBorderExist
|
||||
? UnderlineInputBorder(
|
||||
borderSide:
|
||||
BorderSide(color: Theme.of(context).dividerColor, width: 1.0))
|
||||
: InputBorder.none,
|
||||
enabledBorder: isBorderExist
|
||||
? UnderlineInputBorder(
|
||||
borderSide:
|
||||
decoration: InputDecoration(
|
||||
suffixIcon: SizedBox(
|
||||
width: prefixIconWidth * options.length +
|
||||
(spaceBetweenPrefixIcons * options.length),
|
||||
),
|
||||
hintStyle: TextStyle(
|
||||
fontSize: 16,
|
||||
color: Theme.of(context).primaryTextTheme.caption.color
|
||||
),
|
||||
hintText: placeholder ?? S.current.widgets_address,
|
||||
focusedBorder: isBorderExist
|
||||
? UnderlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).dividerColor,
|
||||
width: 1.0))
|
||||
: InputBorder.none,
|
||||
disabledBorder: isBorderExist
|
||||
? UnderlineInputBorder(
|
||||
borderSide:
|
||||
BorderSide(color: Theme.of(context).dividerColor, width: 1.0))
|
||||
: InputBorder.none,
|
||||
),
|
||||
validator: validator,
|
||||
: InputBorder.none,
|
||||
enabledBorder: isBorderExist
|
||||
? UnderlineInputBorder(
|
||||
borderSide:
|
||||
BorderSide(color: Theme.of(context).dividerColor, width: 1.0))
|
||||
: InputBorder.none,
|
||||
),
|
||||
validator: validator,
|
||||
),
|
||||
Positioned(
|
||||
bottom: 10,
|
||||
right: 0,
|
||||
child: SizedBox(
|
||||
width: prefixIconWidth * options.length +
|
||||
(spaceBetweenPrefixIcons * options.length),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
SizedBox(width: 5),
|
||||
if (this.options.contains(AddressTextFieldOption.qrCode)) ...[
|
||||
Container(
|
||||
width: prefixIconWidth,
|
||||
height: prefixIconHeight,
|
||||
padding: EdgeInsets.only(top: 0),
|
||||
child: InkWell(
|
||||
onTap: () async => _presentQRScanner(context),
|
||||
child: Container(
|
||||
padding: EdgeInsets.all(8),
|
||||
decoration: BoxDecoration(
|
||||
color: buttonColor ?? Theme.of(context).accentTextTheme.title.color,
|
||||
borderRadius:
|
||||
BorderRadius.all(Radius.circular(6))),
|
||||
child: Image.asset('assets/images/qr_code_icon.png')),
|
||||
))
|
||||
],
|
||||
if (this
|
||||
.options
|
||||
.contains(AddressTextFieldOption.addressBook)) ...[
|
||||
Container(
|
||||
width: prefixIconWidth,
|
||||
height: prefixIconHeight,
|
||||
padding: EdgeInsets.only(top: 0),
|
||||
child: InkWell(
|
||||
onTap: () async => _presetAddressBookPicker(context),
|
||||
child: Container(
|
||||
padding: EdgeInsets.all(8),
|
||||
decoration: BoxDecoration(
|
||||
color: buttonColor ?? Theme.of(context).accentTextTheme.title.color,
|
||||
borderRadius:
|
||||
BorderRadius.all(Radius.circular(6))),
|
||||
child: Image.asset(
|
||||
'assets/images/open_book.png')),
|
||||
))
|
||||
],
|
||||
if (this
|
||||
.options
|
||||
.contains(AddressTextFieldOption.subaddressList)) ...[
|
||||
Container(
|
||||
width: prefixIconWidth,
|
||||
height: prefixIconHeight,
|
||||
padding: EdgeInsets.only(top: 0),
|
||||
child: InkWell(
|
||||
onTap: () async => _presetSubaddressListPicker(context),
|
||||
child: Container(
|
||||
padding: EdgeInsets.all(8),
|
||||
decoration: BoxDecoration(
|
||||
color: buttonColor ?? Theme.of(context).accentTextTheme.title.color,
|
||||
borderRadius:
|
||||
BorderRadius.all(Radius.circular(6))),
|
||||
child: Image.asset(
|
||||
'assets/images/receive_icon_raw.png')),
|
||||
))
|
||||
],
|
||||
],
|
||||
),
|
||||
)
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,172 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class StandardListRow extends StatelessWidget {
|
||||
StandardListRow(
|
||||
{@required this.title, @required this.isSelected, this.onTap});
|
||||
|
||||
final String title;
|
||||
final bool isSelected;
|
||||
final void Function(BuildContext context) onTap;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final leading = buildLeading(context);
|
||||
final trailing = buildTrailing(context);
|
||||
|
||||
return InkWell(
|
||||
onTap: () => onTap?.call(context),
|
||||
child: Container(
|
||||
color: _backgroundColor(context),
|
||||
height: 56,
|
||||
padding: EdgeInsets.only(left: 24, right: 24),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: <Widget>[
|
||||
if (leading != null) leading,
|
||||
buildCenter(context, hasLeftOffset: leading != null),
|
||||
if (trailing != null) trailing
|
||||
])));
|
||||
}
|
||||
|
||||
Widget buildLeading(BuildContext context) => null;
|
||||
|
||||
Widget buildCenter(BuildContext context, {@required bool hasLeftOffset}) {
|
||||
// FIXME: find better way for keep text on left side.
|
||||
return Expanded(
|
||||
child: Row(mainAxisAlignment: MainAxisAlignment.start, children: [
|
||||
if (hasLeftOffset) SizedBox(width: 10),
|
||||
Text(title,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: _titleColor(context)))
|
||||
]));
|
||||
}
|
||||
|
||||
Widget buildTrailing(BuildContext context) => null;
|
||||
|
||||
Color _titleColor(BuildContext context) => isSelected
|
||||
? Color.fromRGBO(20, 200, 71, 1)
|
||||
: Theme.of(context).primaryTextTheme.title.color;
|
||||
|
||||
Color _backgroundColor(BuildContext context) {
|
||||
// return Theme.of(context).accentTextTheme.subtitle.decorationColor;
|
||||
return Theme.of(context).accentTextTheme.title.backgroundColor;
|
||||
}
|
||||
}
|
||||
|
||||
class SectionHeaderListRow extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) => Column(children: [
|
||||
StandardListSeparator(),
|
||||
Container(width: double.infinity, height: 40, color: Colors.white),
|
||||
StandardListSeparator()
|
||||
]);
|
||||
}
|
||||
|
||||
class StandardListSeparator extends StatelessWidget {
|
||||
StandardListSeparator({this.padding});
|
||||
|
||||
final EdgeInsets padding;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
height: 1,
|
||||
padding: padding,
|
||||
color: Theme.of(context).accentTextTheme.title.backgroundColor,
|
||||
child: Container(height: 1, color: Color.fromRGBO(219, 227, 243, 1)));
|
||||
}
|
||||
}
|
||||
|
||||
class StandardList extends StatelessWidget {
|
||||
StandardList({@required this.itemCount, @required this.itemBuilder});
|
||||
|
||||
final int itemCount;
|
||||
final IndexedWidgetBuilder itemBuilder;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ListView.separated(
|
||||
separatorBuilder: (_, __) =>
|
||||
StandardListSeparator(padding: EdgeInsets.only(left: 24)),
|
||||
itemCount: itemCount,
|
||||
itemBuilder: itemBuilder);
|
||||
}
|
||||
}
|
||||
|
||||
class SectionStandardListItem {
|
||||
SectionStandardListItem({this.hasFullSeparator = false, this.child});
|
||||
|
||||
final bool hasFullSeparator;
|
||||
final Widget child;
|
||||
}
|
||||
|
||||
class SectionStandardList extends StatelessWidget {
|
||||
SectionStandardList(
|
||||
{@required this.itemCounter,
|
||||
@required this.itemBuilder,
|
||||
@required this.sectionCount,
|
||||
BuildContext context})
|
||||
: totalRows = transform(context, sectionCount, itemCounter, itemBuilder);
|
||||
|
||||
final int sectionCount;
|
||||
final int Function(int sectionIndex) itemCounter;
|
||||
final Widget Function(BuildContext context, int sectionIndex, int itemIndex)
|
||||
itemBuilder;
|
||||
final List<Widget> totalRows;
|
||||
|
||||
static List<Widget> transform(
|
||||
BuildContext context,
|
||||
int sectionCount,
|
||||
int Function(int sectionIndex) itemCounter,
|
||||
Widget Function(BuildContext context, int sectionIndex, int itemIndex)
|
||||
itemBuilder) {
|
||||
final items = <Widget>[];
|
||||
|
||||
for (var sectionIndex = 0; sectionIndex < sectionCount; sectionIndex++) {
|
||||
if (sectionIndex == 0) {
|
||||
items.add(StandardListSeparator());
|
||||
}
|
||||
|
||||
final itemCount = itemCounter(sectionIndex);
|
||||
|
||||
for (var itemIndex = 0; itemIndex < itemCount; itemIndex++) {
|
||||
final item = itemBuilder(context, sectionIndex, itemIndex);
|
||||
|
||||
items.add(item);
|
||||
}
|
||||
|
||||
items.add(sectionIndex + 1 != sectionCount
|
||||
? SectionHeaderListRow()
|
||||
: StandardListSeparator());
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ListView.separated(
|
||||
separatorBuilder: (_, index) {
|
||||
final row = totalRows[index];
|
||||
|
||||
if (row is StandardListSeparator || row is SectionHeaderListRow) {
|
||||
return Container();
|
||||
}
|
||||
|
||||
final nextRow = totalRows[index + 1];
|
||||
|
||||
// If current row is pre last and last row is separator.
|
||||
if (nextRow is StandardListSeparator ||
|
||||
nextRow is SectionHeaderListRow) {
|
||||
return Container();
|
||||
}
|
||||
|
||||
return StandardListSeparator(padding: EdgeInsets.only(left: 24));
|
||||
},
|
||||
itemCount: totalRows.length,
|
||||
itemBuilder: (_, index) => totalRows[index]);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,11 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class StandartListRow extends StatelessWidget {
|
||||
StandartListRow({this.title, this.value, this.isDrawTop, this.isDrawBottom});
|
||||
StandartListRow(
|
||||
{this.title,
|
||||
this.value,
|
||||
this.isDrawTop = false,
|
||||
this.isDrawBottom = false});
|
||||
|
||||
final String title;
|
||||
final String value;
|
||||
@@ -13,17 +17,18 @@ class StandartListRow extends StatelessWidget {
|
||||
return Column(
|
||||
children: <Widget>[
|
||||
isDrawTop
|
||||
? Container(
|
||||
width: double.infinity,
|
||||
height: 1,
|
||||
color: Theme.of(context).dividerColor,
|
||||
)
|
||||
: Offstage(),
|
||||
? Container(
|
||||
width: double.infinity,
|
||||
height: 1,
|
||||
color: Theme.of(context).dividerColor,
|
||||
)
|
||||
: Offstage(),
|
||||
Container(
|
||||
width: double.infinity,
|
||||
color: Theme.of(context).accentTextTheme.title.backgroundColor,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(left: 24, top: 16, bottom: 16, right: 24),
|
||||
padding:
|
||||
const EdgeInsets.only(left: 24, top: 16, bottom: 16, right: 24),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
@@ -31,26 +36,30 @@ class StandartListRow extends StatelessWidget {
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Theme.of(context).primaryTextTheme.caption.color),
|
||||
color:
|
||||
Theme.of(context).primaryTextTheme.caption.color),
|
||||
textAlign: TextAlign.left),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 12),
|
||||
child: Text(value,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Theme.of(context).primaryTextTheme.title.color)),
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.title
|
||||
.color)),
|
||||
)
|
||||
]),
|
||||
),
|
||||
),
|
||||
isDrawBottom
|
||||
? Container(
|
||||
width: double.infinity,
|
||||
height: 1,
|
||||
color: Theme.of(context).dividerColor,
|
||||
)
|
||||
: Offstage(),
|
||||
? Container(
|
||||
width: double.infinity,
|
||||
height: 1,
|
||||
color: Theme.of(context).dividerColor,
|
||||
)
|
||||
: Offstage(),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class TrailButton extends StatelessWidget {
|
||||
TrailButton({
|
||||
@required this.caption,
|
||||
@required this.onPressed
|
||||
});
|
||||
|
||||
final String caption;
|
||||
final VoidCallback onPressed;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
||||
return ButtonTheme(
|
||||
minWidth: double.minPositive,
|
||||
highlightColor: Colors.transparent,
|
||||
splashColor: Colors.transparent,
|
||||
child: FlatButton(
|
||||
padding: EdgeInsets.all(0),
|
||||
child: Text(
|
||||
caption,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).primaryTextTheme.caption.color,
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 14),
|
||||
),
|
||||
onPressed: onPressed),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user