diff --git a/lib/ui/home_page.dart b/lib/ui/home_page.dart index 7bd6c4d0..b05ef24c 100755 --- a/lib/ui/home_page.dart +++ b/lib/ui/home_page.dart @@ -676,6 +676,8 @@ class _AppHomePageState extends State 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())), @@ -833,7 +835,7 @@ class _AppHomePageState extends State if (receive == null) { return; } - Sheets.showAppHeightEightSheet( + Sheets.showAppHeightNineSheet( context: context, widget: receive); }, highlightColor: receive != null diff --git a/lib/ui/receive/receive_sheet.dart b/lib/ui/receive/receive_sheet.dart index e86577fc..f76e5d08 100755 --- a/lib/ui/receive/receive_sheet.dart +++ b/lib/ui/receive/receive_sheet.dart @@ -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(); } @@ -37,6 +51,18 @@ class _ReceiveSheetStateState extends State { // 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 _capturePng() async { if (shareCardKey != null && shareCardKey.currentContext != null) { RenderRepaintBoundary boundary = @@ -59,6 +85,31 @@ class _ReceiveSheetStateState extends State { // 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 @@ -107,6 +158,13 @@ class _ReceiveSheetStateState extends State { ], ), + // Column for Enter Amount container + Column( + children: [ + getEnterAmountContainer(), + ], + ), + // QR which takes all the available space left from the buttons & address text Expanded( child: Center( @@ -142,7 +200,7 @@ class _ReceiveSheetStateState extends State { 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 @@ -359,4 +417,161 @@ class _ReceiveSheetStateState extends State { ], )); } + + 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 ************// + //*************************************************************// }