mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2025-01-10 12:54:38 +00:00
Merge branch 'CAKE-329-unspent-coins-list-UI' into CAKE-330-unspent-coins-details-UI
# Conflicts: # lib/src/screens/unspent_coins/unspent_coins_list_page.dart
This commit is contained in:
commit
febb446c96
7 changed files with 245 additions and 120 deletions
|
@ -44,6 +44,7 @@ class Palette {
|
|||
static const Color dullGray = Color.fromRGBO(98, 98, 98, 1.0);
|
||||
static const Color protectiveBlue = Color.fromRGBO(33, 148, 255, 1.0);
|
||||
static const Color darkBlue = Color.fromRGBO(109, 128, 178, 1.0);
|
||||
static const Color paleCornflowerBlue = Color.fromRGBO(185, 196, 237, 1.0);
|
||||
}
|
||||
|
||||
class PaletteDark {
|
||||
|
|
|
@ -425,7 +425,31 @@ class SendPage extends BasePage {
|
|||
],
|
||||
),
|
||||
),
|
||||
))
|
||||
)),
|
||||
if (sendViewModel.isBitcoinWallet) Padding(
|
||||
padding: EdgeInsets.only(top: 6),
|
||||
child: GestureDetector(
|
||||
onTap: () => Navigator.of(context)
|
||||
.pushNamed(Routes.unspentCoinsList),
|
||||
child: Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'Coin control (optional)',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.white)),
|
||||
Icon(
|
||||
Icons.arrow_forward_ios,
|
||||
size: 12,
|
||||
color: Colors.white,
|
||||
)
|
||||
],
|
||||
)
|
||||
)
|
||||
)
|
||||
],
|
||||
),
|
||||
)
|
||||
|
@ -558,35 +582,22 @@ class SendPage extends BasePage {
|
|||
),
|
||||
bottomSectionPadding:
|
||||
EdgeInsets.only(left: 24, right: 24, bottom: 24),
|
||||
bottomSection: Column(
|
||||
children: [
|
||||
Observer(builder: (_) {
|
||||
return LoadingPrimaryButton(
|
||||
onPressed: () async {
|
||||
if (_formKey.currentState.validate()) {
|
||||
await sendViewModel.createTransaction();
|
||||
}
|
||||
},
|
||||
text: S.of(context).send,
|
||||
color: Theme.of(context).accentTextTheme.body2.color,
|
||||
textColor: Colors.white,
|
||||
isLoading: sendViewModel.state is IsExecutingState ||
|
||||
sendViewModel.state is TransactionCommitting,
|
||||
isDisabled:
|
||||
false // FIXME !(syncStore.status is SyncedSyncStatus),
|
||||
);
|
||||
}),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 12),
|
||||
child: PrimaryButton(
|
||||
onPressed: () => Navigator.of(context).pushNamed(Routes.unspentCoinsList),
|
||||
text: 'Unspent coins',
|
||||
color: Colors.green,
|
||||
textColor: Colors.white,
|
||||
)
|
||||
)
|
||||
],
|
||||
)),
|
||||
bottomSection: Observer(builder: (_) {
|
||||
return LoadingPrimaryButton(
|
||||
onPressed: () async {
|
||||
if (_formKey.currentState.validate()) {
|
||||
await sendViewModel.createTransaction();
|
||||
}
|
||||
},
|
||||
text: S.of(context).send,
|
||||
color: Theme.of(context).accentTextTheme.body2.color,
|
||||
textColor: Colors.white,
|
||||
isLoading: sendViewModel.state is IsExecutingState ||
|
||||
sendViewModel.state is TransactionCommitting,
|
||||
isDisabled:
|
||||
false // FIXME !(syncStore.status is SyncedSyncStatus),
|
||||
);
|
||||
})),
|
||||
));
|
||||
}
|
||||
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
import 'package:cake_wallet/routes.dart';
|
||||
import 'package:cake_wallet/src/screens/unspent_coins/widgets/unspent_coins_list_item.dart';
|
||||
import 'package:cake_wallet/src/widgets/standard_list.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
|
||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||
import 'package:cake_wallet/view_model/unspent_coins/unspent_coins_list_view_model.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
|
||||
class UnspentCoinsListPage extends BasePage {
|
||||
UnspentCoinsListPage({this.unspentCoinsListViewModel});
|
||||
|
@ -13,29 +15,95 @@ class UnspentCoinsListPage extends BasePage {
|
|||
@override
|
||||
String get title => 'Unspent coins';
|
||||
|
||||
@override
|
||||
Widget trailing(BuildContext context) {
|
||||
final questionImage = Image.asset('assets/images/question_mark.png',
|
||||
color: Theme.of(context).primaryTextTheme.title.color);
|
||||
|
||||
return SizedBox(
|
||||
height: 20.0,
|
||||
width: 20.0,
|
||||
child: ButtonTheme(
|
||||
minWidth: double.minPositive,
|
||||
child: FlatButton(
|
||||
highlightColor: Colors.transparent,
|
||||
splashColor: Colors.transparent,
|
||||
padding: EdgeInsets.all(0),
|
||||
onPressed: () => showUnspentCoinsAlert(context),
|
||||
child: questionImage),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
final UnspentCoinsListViewModel unspentCoinsListViewModel;
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) {
|
||||
return Observer(builder: (_) => SectionStandardList(
|
||||
sectionCount: 1,
|
||||
itemCounter: (int _) => unspentCoinsListViewModel.items.length,
|
||||
itemBuilder: (_, __, index) {
|
||||
final item = unspentCoinsListViewModel.items[index];
|
||||
Widget body(BuildContext context) =>
|
||||
UnspentCoinsListForm(unspentCoinsListViewModel);
|
||||
}
|
||||
|
||||
return GestureDetector(
|
||||
onTap: () =>
|
||||
Navigator.of(context).pushNamed(Routes.unspentCoinsDetails,
|
||||
arguments: item),
|
||||
child: UnspentCoinsListItem(
|
||||
address: item.address,
|
||||
amount: item.amount,
|
||||
isFrozen: item.isFrozen,
|
||||
note: item.note,
|
||||
isSending: item.isSending,
|
||||
onCheckBoxTap: (value) {print('CheckBox taped');},
|
||||
));
|
||||
}));
|
||||
class UnspentCoinsListForm extends StatefulWidget {
|
||||
UnspentCoinsListForm(this.unspentCoinsListViewModel);
|
||||
|
||||
final UnspentCoinsListViewModel unspentCoinsListViewModel;
|
||||
|
||||
@override
|
||||
UnspentCoinsListFormState createState() =>
|
||||
UnspentCoinsListFormState(unspentCoinsListViewModel);
|
||||
}
|
||||
|
||||
class UnspentCoinsListFormState extends State<UnspentCoinsListForm> {
|
||||
UnspentCoinsListFormState(this.unspentCoinsListViewModel);
|
||||
|
||||
final UnspentCoinsListViewModel unspentCoinsListViewModel;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
WidgetsBinding.instance.addPostFrameCallback(afterLayout);
|
||||
}
|
||||
|
||||
void afterLayout(dynamic _) {
|
||||
showUnspentCoinsAlert(context);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
padding: EdgeInsets.fromLTRB(24, 12, 24, 24),
|
||||
child: Observer(
|
||||
builder: (_) => ListView.separated(
|
||||
itemCount: unspentCoinsListViewModel.items.length,
|
||||
separatorBuilder: (_, __) =>
|
||||
SizedBox(height: 15),
|
||||
itemBuilder: (_, int index) {
|
||||
final item = unspentCoinsListViewModel.items[index];
|
||||
|
||||
return GestureDetector(
|
||||
onTap: () =>
|
||||
Navigator.of(context).pushNamed(Routes.unspentCoinsDetails,
|
||||
arguments: item),
|
||||
child: UnspentCoinsListItem(
|
||||
address: item.address,
|
||||
amount: item.amount,
|
||||
isSending: item.isSending,
|
||||
onCheckBoxTap: (value) {},
|
||||
));
|
||||
}
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void showUnspentCoinsAlert(BuildContext context) {
|
||||
showPopUp<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertWithOneAction(
|
||||
alertTitle: '',
|
||||
alertContent: 'Information about unspent coins',
|
||||
buttonText: S.of(context).ok,
|
||||
buttonAction: () => Navigator.of(context).pop());
|
||||
});
|
||||
}
|
|
@ -1,74 +1,101 @@
|
|||
import 'package:auto_size_text/auto_size_text.dart';
|
||||
import 'package:cake_wallet/palette.dart';
|
||||
import 'package:cake_wallet/src/widgets/standard_checkbox.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
|
||||
class UnspentCoinsListItem extends StatelessWidget {
|
||||
class UnspentCoinsListItem extends StatefulWidget {
|
||||
UnspentCoinsListItem({
|
||||
@required this.address,
|
||||
@required this.amount,
|
||||
@required this.isFrozen,
|
||||
@required this.note,
|
||||
@required this.isSending,
|
||||
@required this.onCheckBoxTap,
|
||||
});
|
||||
});
|
||||
|
||||
final String address;
|
||||
final String amount;
|
||||
final bool isFrozen;
|
||||
final String note;
|
||||
final bool isSending;
|
||||
final Function(bool) onCheckBoxTap;
|
||||
|
||||
@override UnspentCoinsListItemState createState() =>
|
||||
UnspentCoinsListItemState(
|
||||
address: address,
|
||||
amount: amount,
|
||||
isSending: isSending,
|
||||
onCheckBoxTap: onCheckBoxTap
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
class UnspentCoinsListItemState extends State<UnspentCoinsListItem> {
|
||||
UnspentCoinsListItemState({
|
||||
@required this.address,
|
||||
@required this.amount,
|
||||
@required this.isSending,
|
||||
@required this.onCheckBoxTap,
|
||||
}) : checkBoxValue = isSending;
|
||||
|
||||
static const amountColor = Palette.darkBlueCraiola;
|
||||
static const addressColor = Palette.darkGray;
|
||||
static const selectedItemColor = Palette.paleCornflowerBlue;
|
||||
static const unselectedItemColor = Palette.moderateLavender;
|
||||
|
||||
final String address;
|
||||
final String amount;
|
||||
final bool isSending;
|
||||
final Function(bool) onCheckBoxTap;
|
||||
|
||||
bool checkBoxValue;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final textStyle = TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.title
|
||||
.color);
|
||||
final itemColor = checkBoxValue? selectedItemColor : unselectedItemColor;
|
||||
|
||||
return Container(
|
||||
padding: EdgeInsets.fromLTRB(24, 16, 24, 16),
|
||||
color: Theme.of(context).backgroundColor,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
height: 62,
|
||||
padding: EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.all(Radius.circular(12)),
|
||||
color: itemColor),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
address ?? 'Address',
|
||||
style: textStyle,
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 12),
|
||||
child: Text(
|
||||
amount ?? 'Amount',
|
||||
style: textStyle,
|
||||
)
|
||||
),
|
||||
if (isFrozen ?? false) Padding(
|
||||
padding: EdgeInsets.only(top: 12),
|
||||
child: Text(
|
||||
'Freeze',
|
||||
style: textStyle,
|
||||
)
|
||||
),
|
||||
if (note?.isNotEmpty ?? false) Padding(
|
||||
padding: EdgeInsets.only(top: 12),
|
||||
child: AutoSizeText(
|
||||
note,
|
||||
style: textStyle,
|
||||
maxLines: 1
|
||||
)
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 12),
|
||||
padding: EdgeInsets.only(right: 12),
|
||||
child: StandardCheckbox(
|
||||
value: isSending,
|
||||
caption: 'Sending',
|
||||
onChanged: onCheckBoxTap
|
||||
value: checkBoxValue,
|
||||
onChanged: (value) {
|
||||
onCheckBoxTap(value);
|
||||
checkBoxValue = value;
|
||||
setState(() {});
|
||||
}
|
||||
)
|
||||
),
|
||||
Expanded(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
AutoSizeText(
|
||||
amount ?? 'Amount',
|
||||
style: TextStyle(
|
||||
color: amountColor,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600
|
||||
),
|
||||
maxLines: 1,
|
||||
),
|
||||
AutoSizeText(
|
||||
address ?? 'Address',
|
||||
style: TextStyle(
|
||||
color: addressColor,
|
||||
fontSize: 12,
|
||||
),
|
||||
maxLines: 1,
|
||||
)
|
||||
]
|
||||
)
|
||||
)
|
||||
],
|
||||
)
|
||||
|
|
|
@ -44,9 +44,6 @@ class StandardCheckboxState extends State<StandardCheckbox> {
|
|||
Container(
|
||||
height: 24.0,
|
||||
width: 24.0,
|
||||
margin: EdgeInsets.only(
|
||||
right: 10.0,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
color: Theme.of(context)
|
||||
|
@ -65,14 +62,17 @@ class StandardCheckboxState extends State<StandardCheckbox> {
|
|||
)
|
||||
: Offstage(),
|
||||
),
|
||||
Text(
|
||||
caption,
|
||||
style: TextStyle(
|
||||
fontSize: 16.0,
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.title
|
||||
.color),
|
||||
if (caption.isNotEmpty) Padding(
|
||||
padding: EdgeInsets.only(left: 10),
|
||||
child: Text(
|
||||
caption,
|
||||
style: TextStyle(
|
||||
fontSize: 16.0,
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.title
|
||||
.color),
|
||||
)
|
||||
)
|
||||
],
|
||||
),
|
||||
|
|
|
@ -55,6 +55,8 @@ abstract class SendViewModelBase with Store {
|
|||
_settingsStore.priority[_wallet.type] = priorities.first;
|
||||
}
|
||||
|
||||
isBitcoinWallet = _wallet is BitcoinWallet;
|
||||
|
||||
_setCryptoNumMaximumFractionDigits();
|
||||
}
|
||||
|
||||
|
@ -182,6 +184,9 @@ abstract class SendViewModelBase with Store {
|
|||
@observable
|
||||
PendingTransaction pendingTransaction;
|
||||
|
||||
@observable
|
||||
bool isBitcoinWallet;
|
||||
|
||||
@computed
|
||||
String get balance => _wallet.balance.formattedAvailableBalance ?? '0.0';
|
||||
|
||||
|
|
|
@ -5,32 +5,45 @@ part 'unspent_coins_list_view_model.g.dart';
|
|||
|
||||
const List<Map<String, dynamic>> unspentCoinsMap = [
|
||||
<String, dynamic>{
|
||||
"address" : "11111111111111121111132432432432432432432443124324324234324324324324332424",
|
||||
"amount" : "222",
|
||||
"address" : "bc1qm80mu5p3mf04a7cj7teymasf04dwpc3av2fwtr",
|
||||
"amount" : "0.00358 BTC",
|
||||
"isFrozen" : true,
|
||||
"note" : "333cvgf23132132132132131321321314rwrtdggfdddewq ewqasfdxgdhgfgfszczcxgbhhhbcgbc"},
|
||||
<String, dynamic>{
|
||||
"address" : "444",
|
||||
"amount" : "555",
|
||||
"address" : "bc1qm80mu5p3mf04a7cj7teymasf04dwpc3av2fwtr",
|
||||
"amount" : "0.00567894 BTC",
|
||||
"note" : "sfjskf"},
|
||||
<String, dynamic>{
|
||||
"address" : "777",
|
||||
"amount" : "888",
|
||||
"address" : "bc1qm80mu5p3mf04a7cj7teymasf04dwpc3av2fwtr",
|
||||
"amount" : "0.00087 BTC",
|
||||
"isFrozen" : false},
|
||||
<String, dynamic>{
|
||||
"address" : "11111111111111121111132432432432432432432443124324324234324324324324332424",
|
||||
"amount" : "222",
|
||||
"address" : "bc1qm80mu5p3mf04a7cj7teymasf04dwpc3av2fwtr",
|
||||
"amount" : "0.00012 BTC",
|
||||
"isFrozen" : true,
|
||||
"note" : "333cvgf23132132132132131321321314rwrtdggfdddewq ewqasfdxgdhgfgfszczcxgbhhhbcgbc"},
|
||||
<String, dynamic>{
|
||||
"address" : "444",
|
||||
"amount" : "555",
|
||||
"address" : "bc1qm80mu5p3mf04a7cj7teymasf04dwpc3av2fwtr",
|
||||
"amount" : "0.00574 BTC",
|
||||
"note" : "sffsfsdsgs"},
|
||||
<String, dynamic>{
|
||||
"address" : "777",
|
||||
"amount" : "888",
|
||||
"address" : "bc1qm80mu5p3mf04a7cj7teymasf04dwpc3av2fwtr",
|
||||
"amount" : "0.000482 BTC",
|
||||
"isFrozen" : false},
|
||||
<String, dynamic>{},
|
||||
<String, dynamic>{
|
||||
"address" : "bc1qm80mu5p3mf04a7cj7teymasf04dwpc3av2fwtr",
|
||||
"amount" : "0.00012 BTC",
|
||||
"isFrozen" : true,
|
||||
"note" : "333cvgf23132132132132131321321314rwrtdggfdddewq ewqasfdxgdhgfgfszczcxgbhhhbcgbc"},
|
||||
<String, dynamic>{
|
||||
"address" : "bc1qm80mu5p3mf04a7cj7teymasf04dwpc3av2fwtr",
|
||||
"amount" : "0.00574 BTC",
|
||||
"note" : "sffsfsdsgs"},
|
||||
<String, dynamic>{
|
||||
"address" : "bc1qm80mu5p3mf04a7cj7teymasf04dwpc3av2fwtr",
|
||||
"amount" : "0.000482 BTC",
|
||||
"isFrozen" : false},
|
||||
];
|
||||
|
||||
class UnspentCoinsListViewModel = UnspentCoinsListViewModelBase with _$UnspentCoinsListViewModel;
|
||||
|
|
Loading…
Reference in a new issue