Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

QR amount input #105

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion lib/ui/home_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -676,6 +676,8 @@ class _AppHomePageState extends State<AppHomePage>
painter.toImageData(MediaQuery.of(context).size.width).then((byteData) {
setState(() {
receive = ReceiveSheet(
localCurrency: StateContainer.of(context).curCurrency,
address: StateContainer.of(context).wallet.address,
qrWidget: Container(
width: MediaQuery.of(context).size.width / 2.675,
child: Image.memory(byteData.buffer.asUint8List())),
Expand Down Expand Up @@ -833,7 +835,7 @@ class _AppHomePageState extends State<AppHomePage>
if (receive == null) {
return;
}
Sheets.showAppHeightEightSheet(
Sheets.showAppHeightNineSheet(
context: context, widget: receive);
},
highlightColor: receive != null
Expand Down
219 changes: 217 additions & 2 deletions lib/ui/receive/receive_sheet.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,36 @@ import 'package:auto_size_text/auto_size_text.dart';
import 'package:natrium_wallet_flutter/themes.dart';
import 'package:path_provider/path_provider.dart';
import 'package:share/share.dart';
import 'package:intl/intl.dart';
import 'package:decimal/decimal.dart';
import 'package:natrium_wallet_flutter/localization.dart';
import 'package:natrium_wallet_flutter/model/available_currency.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter/rendering.dart';
import 'package:natrium_wallet_flutter/app_icons.dart';
import 'package:natrium_wallet_flutter/dimens.dart';
import 'package:natrium_wallet_flutter/ui/widgets/app_text_field.dart';
import 'package:natrium_wallet_flutter/ui/widgets/buttons.dart';
import 'package:natrium_wallet_flutter/ui/util/ui_util.dart';
import 'package:natrium_wallet_flutter/ui/util/formatters.dart';
import 'package:natrium_wallet_flutter/ui/receive/share_card.dart';
import 'package:natrium_wallet_flutter/appstate_container.dart';
import 'package:natrium_wallet_flutter/util/numberutil.dart';
import 'package:qr_flutter/qr_flutter.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:flare_flutter/flare_actor.dart';

class ReceiveSheet extends StatefulWidget {
final AvailableCurrency localCurrency;
final String address;
final Widget qrWidget;

ReceiveSheet({this.qrWidget}) : super();
ReceiveSheet(
{@required this.localCurrency,
this.address,
this.qrWidget})
: super();

_ReceiveSheetStateState createState() => _ReceiveSheetStateState();
}
Expand All @@ -37,6 +51,18 @@ class _ReceiveSheetStateState extends State<ReceiveSheet> {
// Timer reference so we can cancel repeated events
Timer _addressCopiedTimer;

FocusNode _receiveAmountFocusNode;
TextEditingController _receiveAmountController;

NumberFormat _localCurrencyFormat;
bool _localCurrencyMode = false;
String _amountHint = "";

String _lastLocalCurrencyAmount = "";
String _lastCryptoAmount = "";

Widget qrWidget;

Future<Uint8List> _capturePng() async {
if (shareCardKey != null && shareCardKey.currentContext != null) {
RenderRepaintBoundary boundary =
Expand All @@ -59,6 +85,31 @@ class _ReceiveSheetStateState extends State<ReceiveSheet> {
// Share card initialization
shareCardKey = GlobalKey();
_showShareCard = false;

// Set initial state of QR widget
qrWidget = widget.qrWidget;

// Set up amount input
_receiveAmountFocusNode = FocusNode();
_receiveAmountController = TextEditingController();

// On amount focus change
_receiveAmountFocusNode.addListener(() {
if (_receiveAmountFocusNode.hasFocus) {
setState(() {
_amountHint = null;
});
} else {
setState(() {
_amountHint = "";
});
}
});

// Set initial currency format
_localCurrencyFormat = NumberFormat.currency(
locale: widget.localCurrency.getLocale().toString(),
symbol: widget.localCurrency.getCurrencySymbol());
}

@override
Expand Down Expand Up @@ -107,6 +158,13 @@ class _ReceiveSheetStateState extends State<ReceiveSheet> {
],
),

// Column for Enter Amount container
Column(
children: <Widget>[
getEnterAmountContainer(),
],
),

// QR which takes all the available space left from the buttons & address text
Expanded(
child: Center(
Expand Down Expand Up @@ -142,7 +200,7 @@ class _ReceiveSheetStateState extends State<ReceiveSheet> {
child: Container(
height: MediaQuery.of(context).size.width / 2.65,
width: MediaQuery.of(context).size.width / 2.65,
child: widget.qrWidget,
child: this.qrWidget,
),
),
// Outer ring
Expand Down Expand Up @@ -359,4 +417,161 @@ class _ReceiveSheetStateState extends State<ReceiveSheet> {
],
));
}

String _convertLocalCurrencyToCrypto() {
String convertedAmt = _receiveAmountController.text.replaceAll(",", ".");
convertedAmt = NumberUtil.sanitizeNumber(convertedAmt);
if (convertedAmt.isEmpty) {
return "";
}
Decimal valueLocal = Decimal.parse(convertedAmt);
Decimal conversion = Decimal.parse(
StateContainer.of(context).wallet.localCurrencyConversion);
return NumberUtil.truncateDecimal(valueLocal / conversion).toString();
}

String _convertCryptoToLocalCurrency() {
String convertedAmt = NumberUtil.sanitizeNumber(_receiveAmountController.text,
maxDecimalDigits: 2);
if (convertedAmt.isEmpty) {
return "";
}
Decimal valueCrypto = Decimal.parse(convertedAmt);
Decimal conversion = Decimal.parse(
StateContainer.of(context).wallet.localCurrencyConversion);
convertedAmt =
NumberUtil.truncateDecimal(valueCrypto * conversion, digits: 2)
.toString();
convertedAmt =
convertedAmt.replaceAll(".", _localCurrencyFormat.symbols.DECIMAL_SEP);
convertedAmt = _localCurrencyFormat.currencySymbol + convertedAmt;
return convertedAmt;
}

void toggleLocalCurrency() {
// Keep a cache of previous amounts because, it's kinda nice to see approx what nano is worth
// this way you can tap button and tap back and not end up with X.9993451 NANO
if (_localCurrencyMode) {
// Switching to crypto-mode
String cryptoAmountStr;
// Check out previous state
if (_receiveAmountController.text == _lastLocalCurrencyAmount) {
cryptoAmountStr = _lastCryptoAmount;
} else {
_lastLocalCurrencyAmount = _receiveAmountController.text;
_lastCryptoAmount = _convertLocalCurrencyToCrypto();
cryptoAmountStr = _lastCryptoAmount;
}
setState(() {
_localCurrencyMode = false;
});
Future.delayed(Duration(milliseconds: 50), () {
_receiveAmountController.text = cryptoAmountStr;
_receiveAmountController.selection = TextSelection.fromPosition(
TextPosition(offset: cryptoAmountStr.length));
});
} else {
// Switching to local-currency mode
String localAmountStr;
// Check our previous state
if (_receiveAmountController.text == _lastCryptoAmount) {
localAmountStr = _lastLocalCurrencyAmount;
} else {
_lastCryptoAmount = _receiveAmountController.text;
_lastLocalCurrencyAmount = _convertCryptoToLocalCurrency();
localAmountStr = _lastLocalCurrencyAmount;
}
setState(() {
_localCurrencyMode = true;
});
Future.delayed(Duration(milliseconds: 50), () {
_receiveAmountController.text = localAmountStr;
_receiveAmountController.selection = TextSelection.fromPosition(
TextPosition(offset: localAmountStr.length));
});
}
}

void redrawQr() {
String raw;
if (_localCurrencyMode) {
_lastLocalCurrencyAmount = _receiveAmountController.text;
_lastCryptoAmount = _convertLocalCurrencyToCrypto();
raw = _lastCryptoAmount.length > 0
? NumberUtil.getAmountAsRaw(_lastCryptoAmount)
: '';
} else {
raw = _receiveAmountController.text.length > 0
? NumberUtil.getAmountAsRaw(_receiveAmountController.text)
: '';
}
this.paintQrCode(address: widget.address, amount: raw);
}

void paintQrCode({String address, String amount}) {
QrPainter painter = QrPainter(
data: amount != '' ? 'nano:' + address + '?amount=' + amount : address,
version: amount != '' ? 9 : 6,
gapless: false,
errorCorrectionLevel: QrErrorCorrectLevel.Q,
);
painter.toImageData(MediaQuery.of(context).size.width).then((byteData) {
setState(() {
this.qrWidget = Container(
width: MediaQuery.of(context).size.width / 2.675,
child: Image.memory(byteData.buffer.asUint8List())
);
});
});
}

//************ Enter Amount Container Method ************//
//*******************************************************//
getEnterAmountContainer() {
return AppTextField(
focusNode: _receiveAmountFocusNode,
controller: _receiveAmountController,
topMargin: 30,
cursorColor: StateContainer.of(context).curTheme.primary,
style: TextStyle(
fontWeight: FontWeight.w700,
fontSize: 16.0,
color: StateContainer.of(context).curTheme.primary,
fontFamily: 'NunitoSans',
),
inputFormatters: [
LengthLimitingTextInputFormatter(13),
_localCurrencyMode
? CurrencyFormatter(
decimalSeparator:
_localCurrencyFormat.symbols.DECIMAL_SEP,
commaSeparator: _localCurrencyFormat.symbols.GROUP_SEP,
maxDecimalDigits: 2)
: CurrencyFormatter(
maxDecimalDigits: NumberUtil.maxDecimalDigits),
LocalCurrencyFormatter(
active: _localCurrencyMode,
currencyFormat: _localCurrencyFormat)
],
onChanged: (text) {
this.redrawQr();
},
textInputAction: TextInputAction.next,
maxLines: null,
autocorrect: false,
hintText:
_amountHint == null ? "" : AppLocalization.of(context).enterAmount +
(_localCurrencyMode ?' (' + _localCurrencyFormat.currencySymbol +')' : ' (NANO)'),
prefixButton: TextFieldButton(
icon: AppIcons.swapcurrency,
onPressed: () {
toggleLocalCurrency();
},
),
fadeSuffixOnCondition: true,
keyboardType: TextInputType.numberWithOptions(decimal: true),
textAlign: TextAlign.center,
);
} //************ Enter Address Container Method End ************//
//*************************************************************//
}