import 'package:checker/base/base_screen.dart'; import 'package:checker/db.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'dart:convert'; import 'dart:core'; import 'dart:async'; import 'package:checker/resources.dart'; import 'package:checker/strings.dart'; import 'package:checker/common.dart'; import 'package:checker/consts.dart'; import 'package:checker/network.dart'; import 'package:checker/base/base_state.dart'; import 'package:checker/screens/purchase_success.dart'; /// Экран проведения покупки. class PurchaseScreen extends BaseScreen { PurchaseScreen(helper, app, this.user, this.card) : super(helper, app); final String user; final String card; @override State createState() => new PurchaseScreenState(helper, app, user, card); } class PurchaseScreenState extends BaseState { /// Объект, помогающий вручную изменять введенный пользователем текст. /// Используется для форматирования введенных пользователем данных /// (удаляет запрещенные символы до их отображаения). TextEditingController controller = new TextEditingController(); TextEditingController bonusController = new TextEditingController(); bool isAutomaticallyImplyLeading() => false; PurchaseScreenState(SqliteHelper helper, String app, String userString, String card) : super(helper, app) { this.user = json.decode(userString); this.card = card; } @override void initState() { loading = true; requestAsyncData(user); buildFocusNode(); scrollController = new ScrollController(); super.initState(); } @override Widget build(BuildContext ctx) { return getMainWidget(); } bool purchaseInProgress = false; ScrollController scrollController; Map user; String card = ''; String loyalityType = ''; String loyalty = ''; String bonus = ''; List coupons = []; ListView listView; @override Widget getScreenContent() { List widgetList = []; widgetList.add(getValueWithDescription( StringsLocalization.buyer(), user['first_name'] == null ? '' : user['first_name'] )); widgetList.add(getValueWithDescription(StringsLocalization.card(), card)); if (app != 'crypto') { widgetList.add(getValueWithDescription(StringsLocalization.reward(), loyalty)); } if (app != 'crypto') { if (bonus.length > 0) { widgetList.add(getValueWithDescription(StringsLocalization.bonus(), bonus)); } } widgetList.add(getHintLabel()); widgetList.add(getInputField()); // Нельзя добавить еще одно поле таким же способом if ((app != 'autobonus') || (app != 'crypto')) { if (this.loyalityType == 'bonus') { widgetList.add(getBonusInputField()); } if (this.coupons.length > 0) { widgetList.add(getItemTitle(StringsLocalization.selectCoupons())); this.coupons.forEach((couponItem) { widgetList.add(getItemToggleSwitch( couponItem['offer_name'], couponItem['coupon_condition'], couponItem['isSet'], (val) { setState(() { couponItem['isSet'] = val; }); } )); }); } } widgetList.add(wrapButton(getScreenMargins(36.0), getCompleteButton())); widgetList.add(wrapButton( getScreenMargins(24.0), getScanButton( context, StringsLocalization.scan(), Resources.getPrimaryColor(app) ) )); if (this.coupons.length == 0) { widgetList.add(new Container(height: 50.0,)); } listView = new ListView(children: widgetList, controller: scrollController); return listView; } getBonusInputField() { var bonusTextField = new TextField( keyboardType: TextInputType.number, decoration: new InputDecoration.collapsed( hintText: (app != 'crypto') ? StringsLocalization.bonusHint() : StringsLocalization.joysHint(), hintStyle: new TextStyle(color: greyTextColor, fontSize: 16.0) ), focusNode: bonusFocusNode, controller: bonusController, onSubmitted: (String text) { setState(() { bonusController.text = text; }); }, textAlign: TextAlign.center, ); return new Column( children: [ new Container( margin: new EdgeInsets.only( left: verticalMargin, right: verticalMargin, top: verticalMargin ), padding: getInputFieldContainerPadding(), decoration: getInputFieldContainerDecoration(), child: bonusTextField ), new Container( margin: new EdgeInsets.only( top: 5.0 ), child: new Text( (app == 'crypto') ? " " : StringsLocalization.bonusExplanation(), textAlign: TextAlign.center, overflow: TextOverflow.ellipsis, style: new TextStyle(color: greyTextColor, fontSize: 11.0), ) ) ], ); } getScreenMargins(double top) { double side = 42.0; return new EdgeInsets.only(top: top, left: side, right: side); } getCompleteButton() { return buildRaisedButton( StringsLocalization.completePurchase(), () => onPurchaseClick()); } Widget getScanButton(BuildContext context, String title, Color textColor) { return new Container( height: buttonHeight, child: new FlatButton( child: new Text(title, style: new TextStyle(color: textColor)), onPressed: () => restartScanner()), decoration: new BoxDecoration( border: new Border.all( color: Resources.getButtonColor(app), width: 1.0), borderRadius: new BorderRadius.all(new Radius.circular(4.0)))); } @override String getTitle() { return StringsLocalization.carryingPurchase(); } @override getHintString() { return StringsLocalization.sum(); } @override getTextWidget() { return new TextField( keyboardType: TextInputType.number, decoration: new InputDecoration.collapsed( hintText: getHintString(), hintStyle: new TextStyle(color: greyTextColor, fontSize: 16.0)), controller: controller, focusNode: sumFocusNode, onSubmitted: (String text) { setState(() { controller.text = _parseSum(text); }); }, textAlign: TextAlign.center); } requestAsyncData(Map user) async { bool showBonus = await platform.invokeMethod('showBonus'); if (await platform.invokeMethod('isOnline')) { var response, couponResponse; print(user['coupons_url']); try { var token = await helper.getToken(); response = await getLoyaltyRequest(user['loyalty_url'], token); couponResponse = await getCouponsRequest( user['coupons_url'] + '?status=ACTIVE', token ); } catch(error) { print(error.toString()); } Map loyality = json.decode(response.body); Map coupons = json.decode(couponResponse.body); coupons['results'].forEach((couponItem) { couponItem['isSet'] = false; }); setState(() { loading = false; this.coupons = coupons['results']; this.loyalityType = loyality['type']; setBonuses(loyality, showBonus); }); } } // TODO: Переделать, если потребуется String _cleanupNumber(String text) { String tmp = text .replaceAll(' ', '') .replaceAll('-', '') .replaceAll(',', '.') .replaceAll('..', '.'); while (tmp.indexOf('..') != -1) { tmp = tmp.replaceAll('..', '.'); } return tmp; } _parseSum(String input) { num sumTotal = 0.0; String text = _cleanupNumber(input); try { sumTotal = num.parse(text); } catch (exception) { print(exception); try { int idx = text.indexOf('.'); String integerPart = text.substring(0, idx); String fractionalPart = text.substring(idx + 1, text.length); if (fractionalPart.length > 2) { fractionalPart = fractionalPart.substring(0, 2); } return '$integerPart.$fractionalPart'; } catch (exception) { print(exception); } } print(sumTotal.toStringAsFixed(2)); return sumTotal.toStringAsFixed(2); } onPurchaseClick() { String val = _parseSum(controller.text); helper.getCurrency().then((currency) { print(currency.toString()); showDialog( context: context, builder: (_) => new AlertDialog( title: new Text(StringsLocalization.confirmation()), content: new Text( StringsLocalization.confirmPurchase(val, currency) ), actions: [ new FlatButton( child: new Text(StringsLocalization.no()), onPressed: () { Navigator.of(context).pop(); }, ), new FlatButton( child: new Text(StringsLocalization.yes()), onPressed: () { Navigator.of(context).pop(); purchase(val); }, ) ])); }); } apiErrorAlert(String errorText) { showDialog( context: context, builder: (_) => new AlertDialog( content: new Text(errorText), actions: [ new FlatButton( child: new Text('Ok'), onPressed: () { Navigator.of(context).pop(); }, ) ] ) ); } purchase(String sumTotal) async { setState(() { loading = true; }); if (await platform.invokeMethod('isOnline')) { if (!purchaseInProgress) { purchaseInProgress = true; String token = await helper.getToken(); var result = await helper.getMerchantID(); var currency = await helper.getCurrency(); var couponIds = []; this.coupons.forEach((item) { if (item['isSet'] == true) couponIds.add(item['id']); }); print(couponIds); var body = { 'doc_id': result, 'curr_iso_code': currency.toString(), 'commit': 'true', 'sum_total': sumTotal, }; if (bonusController.text.length > 0) { body['bonus_payment'] = bonusController.text; } if (couponIds.length > 0) { body['coupons'] = couponIds.join(","); } var purchaseResponse; Map purchase; try { purchaseResponse = await getPurchaseRequest(user['purchases_url'], body, token); purchase = json.decode(purchaseResponse.body); } catch(error) { purchaseInProgress = false; print(error.toString()); } if (purchase.containsKey('errors')) { setState(() { loading = false; }); List errors = purchase['errors']; purchaseInProgress = false; apiErrorAlert(errors[0]); } else { var couponsResponse; try { couponsResponse = await getCouponsRequest(purchase['coupons_url'], token); print(couponsResponse.body); } catch(error) { purchaseInProgress = false; print(error.toString()); } Map coupons = json.decode(couponsResponse.body); new Future.delayed(const Duration(milliseconds: 200), () { print('show purchase success!'); var route = new MaterialPageRoute(builder: (BuildContext context) => new PurchaseSuccessScreen( sumTotal, user['first_name'] == null ? '' : user['first_name'], helper, app, purchase, coupons['results'] ), fullscreenDialog: true); Navigator.of(context).push(route).then((token) { Navigator.of(context).pop(token); }); }); } } } } void setBonuses(Map bonuses, bool showBonus) { print('loyalityType ' + this.loyalityType); if (bonuses['type'] == 'amount') { this.loyalty = '${user['discount']}%'; } else { double loyaltyVal = (double.parse(bonuses['amount_to_bonus'][1]) / bonuses['amount_to_bonus'][0]) * 100; this.loyalty = '${loyaltyVal.toStringAsFixed(0)}%'; } if (showBonus && (this.loyalityType == 'bonus')) { this.bonus = '${user['bonus']}'; } print('loyalty ' + this.loyalty); print('bonus ' + this.bonus); } restartScanner() { helper.getToken().then((token) { Navigator.of(context).pop(token); }); } FocusNode bonusFocusNode = new FocusNode(); FocusNode sumFocusNode = new FocusNode(); // TODO: Удалить дублирующийся код. void buildFocusNode() { var pos = this.coupons.length > 0 ? 150.0 : 100.0; sumFocusNode.addListener(() { setState(() { if (sumFocusNode.hasFocus && bonusFocusNode.hasFocus) { bonusFocusNode.unfocus(); } if (sumFocusNode.hasFocus) { scrollController.animateTo(pos, duration: new Duration(seconds: 1), curve: Curves.ease); } }); }); bonusFocusNode.addListener(() { setState(() { if (bonusFocusNode.hasFocus && sumFocusNode.hasFocus) { sumFocusNode.unfocus(); } if (bonusFocusNode.hasFocus) { scrollController.animateTo(pos, duration: new Duration(seconds: 1), curve: Curves.ease); } }); }); } }