diff --git a/android/app/build.gradle b/android/app/build.gradle index 743b022..1032764 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -211,5 +211,6 @@ dependencies { } repositories { + google() mavenCentral() } diff --git a/android/app/src/main/java/com/dinect/checker/MainActivity.java b/android/app/src/main/java/com/dinect/checker/MainActivity.java index 8566293..be9a587 100644 --- a/android/app/src/main/java/com/dinect/checker/MainActivity.java +++ b/android/app/src/main/java/com/dinect/checker/MainActivity.java @@ -38,6 +38,7 @@ public class MainActivity extends FlutterActivity { private MethodChannel mChannel; private Map mScannerArgs; + private Result scannerResult; @Override protected void onCreate(Bundle savedInstanceState) { @@ -59,6 +60,7 @@ public class MainActivity extends FlutterActivity { result.success(BuildConfig.currency); break; case "startScanner": + scannerResult = result; startScannerActivity(call); break; case "isOnline": @@ -169,7 +171,7 @@ public class MainActivity extends FlutterActivity { protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == START_SCANNER_REQUEST_CODE) { if (resultCode == RESULT_CANCELED) { - finish(); + scannerResult.error("Scanning is cancelled", null, null); } else if (resultCode == RESULT_OK) { if (data != null && data.getExtras() != null) { String user = data.getExtras().getString("user", null); @@ -178,7 +180,9 @@ public class MainActivity extends FlutterActivity { ArrayList args = new ArrayList<>(2); args.add(user); args.add(card); - mChannel.invokeMethod("purchase", args); + if(scannerResult != null) { + scannerResult.success(args); + } } else { String menuItem = data.getExtras().getString("item", null); if (menuItem != null) { diff --git a/android/build.gradle b/android/build.gradle index a7244c4..a2b6e7a 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -13,8 +13,8 @@ buildscript { allprojects { repositories { - jcenter() google() + jcenter() } } diff --git a/assets/values-en/strings.xml b/assets/values-en/strings.xml index b2b3ee7..88d911c 100644 --- a/assets/values-en/strings.xml +++ b/assets/values-en/strings.xml @@ -162,4 +162,7 @@ You bonus balance will be changed and you will have %s bonus points. Return confirmed Purchase, which cost %s on %s is returned. Points + Cancel + Are you sure you want to cancel this purchase? + Total amount: %s %s, with discount of %s %s (%s%%) diff --git a/assets/values-es/strings.xml b/assets/values-es/strings.xml index 4ed0d1d..95459b9 100644 --- a/assets/values-es/strings.xml +++ b/assets/values-es/strings.xml @@ -158,4 +158,7 @@ Se cambiará su saldo de bonificación y tendrá %s puntos de bonificación.Regreso confirmado Compra, cuyo costo %s en %s se devuelve. Puntos + Cancelar + ¿Seguro que quieres cancelar esta compra? + Cantidad total: %s %s, con descuento de %s %s (%s%%) diff --git a/assets/values-ru/strings.xml b/assets/values-ru/strings.xml index 0397885..5b2d2fe 100644 --- a/assets/values-ru/strings.xml +++ b/assets/values-ru/strings.xml @@ -161,4 +161,7 @@ Возврат подтвержден Покупка на сумму %s от %s возвращена. Баллы + Отмена + Вы уверены что хотите отменить покупку? + Итого покупка: %s %s, Со скидкой %s %s (%s%%) diff --git a/assets/values-ua/strings.xml b/assets/values-ua/strings.xml index b733dd9..d712144 100644 --- a/assets/values-ua/strings.xml +++ b/assets/values-ua/strings.xml @@ -163,4 +163,7 @@ Повернення підтверджено Повертається придбання, вартість якого %s на %s. Бонусні бали + Скасувати + Дійсно скасувати цю покупку? + Загальна кількість: %s %s, зі знижкою %s %s (%s%%) diff --git a/ios/Runner/AppDelegate.m b/ios/Runner/AppDelegate.m index a583bf2..bd3e706 100644 --- a/ios/Runner/AppDelegate.m +++ b/ios/Runner/AppDelegate.m @@ -12,9 +12,7 @@ FlutterMethodChannel* platformChannel = [FlutterMethodChannel methodChannelWithName:@"com.dinect.checker/instance_id" - binaryMessenger:controller]; - - __weak FlutterMethodChannel* weekPlatformChannel = platformChannel; + binaryMessenger:controller.binaryMessenger]; NSString *bundleIdentifier = [[NSBundle mainBundle] bundleIdentifier]; @@ -116,7 +114,7 @@ result(buildSettings[@"currency"]); } else if ([@"startScanner" isEqualToString:call.method]) { ScannerViewController *modalViewController = [[ScannerViewController alloc] initWithStrings:call.arguments]; - modalViewController.platformChannel = weekPlatformChannel; + modalViewController.platformChannel = platformChannel; [controller presentViewController:modalViewController animated:YES completion:nil]; } else if ([@"isOnline" isEqualToString:call.method]) { result(@YES); diff --git a/ios/Runner/ScannerViewController.swift b/ios/Runner/ScannerViewController.swift index 53448b9..0782408 100644 --- a/ios/Runner/ScannerViewController.swift +++ b/ios/Runner/ScannerViewController.swift @@ -272,7 +272,7 @@ import ZXingObjC } else { print("Result is not nil (ios code)"); self.dismiss(animated: true) { - self.platformChannel?.invokeMethod("purchase", arguments: [result, str]) + self.platformChannel?.invokeMethod("scanSuccess", arguments: [result!, str]) } } }) diff --git a/lib/screens/finish_registration.dart b/lib/screens/finish_registration.dart index e9435e5..6cc42ef 100644 --- a/lib/screens/finish_registration.dart +++ b/lib/screens/finish_registration.dart @@ -1,5 +1,4 @@ import 'dart:convert'; -import 'dart:async'; import 'package:checker/base/base_screen.dart'; import 'package:checker/base/base_state.dart'; diff --git a/lib/screens/purchase.dart b/lib/screens/purchase.dart index ee7afc8..4a8016f 100644 --- a/lib/screens/purchase.dart +++ b/lib/screens/purchase.dart @@ -1,52 +1,59 @@ -import 'package:checker/base/base_screen.dart'; -import 'package:checker/db.dart'; -import 'package:checker/screens/return.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; +import 'dart:async'; import 'dart:convert'; import 'dart:core'; -import 'dart:async'; -import 'package:checker/resources.dart'; -import 'package:checker/strings.dart'; +import 'package:checker/base/base_screen.dart'; +import 'package:checker/base/base_state.dart'; import 'package:checker/common.dart'; import 'package:checker/consts.dart'; +import 'package:checker/db.dart'; import 'package:checker/network.dart'; -import 'package:checker/base/base_state.dart'; +import 'package:checker/resources.dart'; import 'package:checker/screens/purchase_success.dart'; +import 'package:checker/screens/purchase_sum.dart'; +import 'package:checker/screens/return.dart'; +import 'package:checker/strings.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + +typedef PurchaseResponseCallback = void Function(Map, String, String); /// Экран проведения покупки. class PurchaseScreen extends BaseScreen { - - PurchaseScreen(helper, app, this.user, this.card) : super(helper, app); + PurchaseScreen(helper, app, this.user, this.card, this.sum) + : super(helper, app); final String user; final String card; + final String sum; @override - State createState() => new PurchaseScreenState(helper, app, user, card); + State createState() => + new PurchaseScreenState(helper, app, user, card); } class PurchaseScreenState extends BaseState { /// Объект, помогающий вручную изменять введенный пользователем текст. /// Используется для форматирования введенных пользователем данных /// (удаляет запрещенные символы до их отображаения). - TextEditingController controller = new TextEditingController(); + TextEditingController controller; TextEditingController bonusController = new TextEditingController(); bool isAutomaticallyImplyLeading() => false; - PurchaseScreenState(SqliteHelper helper, String app, String userString, String card) : super(helper, app) { + PurchaseScreenState( + SqliteHelper helper, String app, String userString, String card) + : super(helper, app) { this.user = json.decode(userString); this.card = card; } @override void initState() { + controller = new TextEditingController(text: widget.sum ?? ""); loading = true; requestAsyncData(user); - loading = false; buildFocusNode(); scrollController = new ScrollController(); super.initState(); @@ -70,21 +77,22 @@ class PurchaseScreenState extends BaseState { @override Widget getScreenContent() { List widgetList = []; - widgetList.add(getValueWithDescription( - StringsLocalization.buyer(), - user['first_name'] == null ? '' : user['first_name'] - )); + 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') { + widgetList + .add(getValueWithDescription(StringsLocalization.reward(), loyalty)); if (bonus.length > 0) { - widgetList.add(getValueWithDescription(StringsLocalization.bonus(), bonus)); + widgetList + .add(getValueWithDescription(StringsLocalization.bonus(), bonus)); } } widgetList.add(getHintLabel()); - widgetList.add(getInputField()); // Нельзя добавить еще одно поле таким же способом + widgetList.add( + getInputField()); // Нельзя добавить еще одно поле таким же способом if ((app != 'autobonus') && (app != 'crypto')) { if (this.loyalityType == 'bonus') { @@ -94,16 +102,12 @@ class PurchaseScreenState extends BaseState { 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(getItemToggleSwitch(couponItem['offer_name'], + couponItem['coupon_condition'], couponItem['isSet'], (val) { + setState(() { + couponItem['isSet'] = val; + }); + })); }); } } @@ -111,16 +115,14 @@ class PurchaseScreenState extends BaseState { widgetList.add(wrapButton(getScreenMargins(36.0), getCompleteButton())); widgetList.add(wrapButton( - getScreenMargins(24.0), - getScanButton( - context, - StringsLocalization.scan(), - Resources.getPrimaryColor(app) - ) - )); + getScreenMargins(24.0), + getCancelScanButton(context, StringsLocalization.cancel(), + Resources.getPrimaryColor(app)))); if (this.coupons.length == 0) { - widgetList.add(new Container(height: 50.0,)); + widgetList.add(new Container( + height: 50.0, + )); } listView = new ListView(children: widgetList, controller: scrollController); @@ -131,8 +133,10 @@ class PurchaseScreenState extends BaseState { void openReturnScreen() { Future.delayed(const Duration(milliseconds: 200), () { - Route route = MaterialPageRoute(builder: (BuildContext context) => - ReturnScreen(helper, app, user['id'], user['bonus']), fullscreenDialog: true); + Route route = MaterialPageRoute( + builder: (BuildContext context) => + ReturnScreen(helper, app, user['id'], user['bonus']), + fullscreenDialog: true); Navigator.push(context, route); }); } @@ -141,9 +145,8 @@ class PurchaseScreenState extends BaseState { var bonusTextField = new TextField( keyboardType: TextInputType.number, decoration: new InputDecoration.collapsed( - hintText: StringsLocalization.bonusHint(), - hintStyle: new TextStyle(color: greyTextColor, fontSize: 16.0) - ), + hintText: StringsLocalization.bonusHint(), + hintStyle: new TextStyle(color: greyTextColor, fontSize: 16.0)), focusNode: bonusFocusNode, controller: bonusController, onSubmitted: (String text) { @@ -157,26 +160,21 @@ class PurchaseScreenState extends BaseState { return new Column( children: [ new Container( - margin: new EdgeInsets.only( - left: verticalMargin, - right: verticalMargin, - top: verticalMargin - ), - padding: getInputFieldContainerPadding(), - decoration: getInputFieldContainerDecoration(), - child: bonusTextField - ), + 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( - StringsLocalization.bonusExplanation(), - textAlign: TextAlign.center, - overflow: TextOverflow.ellipsis, - style: new TextStyle(color: greyTextColor, fontSize: 11.0), - ) - ) + margin: new EdgeInsets.only(top: 5.0), + child: new Text( + StringsLocalization.bonusExplanation(), + textAlign: TextAlign.center, + overflow: TextOverflow.ellipsis, + style: new TextStyle(color: greyTextColor, fontSize: 11.0), + )) ], ); } @@ -188,19 +186,20 @@ class PurchaseScreenState extends BaseState { getCompleteButton() { return buildRaisedButton( - StringsLocalization.completePurchase(), () => onPurchaseClick()); + StringsLocalization.completePurchase(), () => onPurchaseClick()); } - Widget getScanButton(BuildContext context, String title, Color textColor) { + Widget getCancelScanButton( + 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)))); + height: buttonHeight, + child: new FlatButton( + child: new Text(title, style: new TextStyle(color: textColor)), + onPressed: () => restartFlow()), + decoration: new BoxDecoration( + border: new Border.all( + color: Resources.getButtonColor(app), width: 1.0), + borderRadius: new BorderRadius.all(new Radius.circular(4.0)))); } @override @@ -216,18 +215,18 @@ class PurchaseScreenState extends BaseState { @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); + 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 { @@ -239,10 +238,8 @@ class PurchaseScreenState extends BaseState { var token = await helper.getToken(); response = await getLoyaltyRequest(user['loyalty_url'], token); couponResponse = await getCouponsRequest( - user['coupons_url'] + '?status=ACTIVE', - token - ); - } catch(error) { + user['coupons_url'] + '?status=ACTIVE', token); + } catch (error) { print(error.toString()); } Map loyality = json.decode(response.body); @@ -263,10 +260,10 @@ class PurchaseScreenState extends BaseState { // TODO: Переделать, если потребуется String _cleanupNumber(String text) { String tmp = text - .replaceAll(' ', '') - .replaceAll('-', '') - .replaceAll(',', '.') - .replaceAll('..', '.'); + .replaceAll(' ', '') + .replaceAll('-', '') + .replaceAll(',', '.') + .replaceAll('..', '.'); while (tmp.indexOf('..') != -1) { tmp = tmp.replaceAll('..', '.'); @@ -299,59 +296,94 @@ class PurchaseScreenState extends BaseState { } onPurchaseClick() { + String val = _parseSum(controller.text); + purchase(val, false, _showPrecalculatedValues); + } + + void _showPrecalculatedValues(Map response, String sumTotal, String token) { String val = _parseSum(controller.text); helper.getCurrency().then((currency) { print(currency.toString()); + final String totalAmount = response['sum_total']; + final String totalDiscount = response['sum_discount']; + final String discount = (response['discount'] as int).toString(); + print(response); 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); - }, - ) - ])); + context: context, + builder: (_) => new AlertDialog( + title: new Text(StringsLocalization.confirmation()), + content: Text(StringsLocalization.purchaseDetails( + totalAmount, totalDiscount, discount, 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, true, _paymentConfirmed); + }, + ) + ])); + }); + } + + void _paymentConfirmed(Map purchase, String sumTotal, String token) async { + 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); + }); }); } 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(); - }, - ) - ] - ) - ); + 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 { + purchase( + String sumTotal, bool commit, PurchaseResponseCallback callback) async { setState(() { loading = true; }); if (await platform.invokeMethod('isOnline')) { if (!purchaseInProgress) { - purchaseInProgress = true; + purchaseInProgress = commit; String token = await helper.getToken(); var result = await helper.getMerchantID(); @@ -367,10 +399,13 @@ class PurchaseScreenState extends BaseState { var body = { 'doc_id': result, 'curr_iso_code': currency.toString(), - 'commit': 'true', 'sum_total': sumTotal, }; + if (commit) { + body['commit'] = 'true'; + } + if (bonusController.text.length > 0) { body['bonus_payment'] = bonusController.text; } @@ -383,9 +418,10 @@ class PurchaseScreenState extends BaseState { Map purchase; try { - purchaseResponse = await getPurchaseRequest(user['purchases_url'], body, token); + purchaseResponse = + await getPurchaseRequest(user['purchases_url'], body, token); purchase = json.decode(purchaseResponse.body); - } catch(error) { + } catch (error) { purchaseInProgress = false; print(error.toString()); } @@ -398,32 +434,7 @@ class PurchaseScreenState extends BaseState { 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); - }); - }); + callback(purchase, sumTotal, token); } } } @@ -432,24 +443,49 @@ class PurchaseScreenState extends BaseState { void setBonuses(Map bonuses, bool showBonus) { print('loyalityType ' + this.loyalityType); if (bonuses['type'] == 'amount') { - this.loyalty = '${user['discount']}%'; + setState(() => 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)}%'; + bonuses['amount_to_bonus'][0]) * + 100; + setState(() => this.loyalty = '${loyaltyVal.toStringAsFixed(0)}%'); } if (showBonus && (this.loyalityType == 'bonus')) { - this.bonus = '${user['bonus']}'; + setState(() => this.bonus = '${user['bonus']}'); } print('loyalty ' + this.loyalty); print('bonus ' + this.bonus); } - restartScanner() { - helper.getToken().then((token) { - Navigator.of(context).pop(token); - }); + restartFlow() { + showDialog( + context: context, + builder: (context) => AlertDialog( + content: Text(StringsLocalization.cancelDialog()), + 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(); + helper.getToken().then((token) { + Navigator.pushAndRemoveUntil( + context, + MaterialPageRoute( + builder: (context) => PurchaseSumScreen( + widget.helper, widget.app, token)), + (route) => false); + }); + }, + ) + ], + )); } FocusNode bonusFocusNode = new FocusNode(); @@ -457,34 +493,22 @@ class PurchaseScreenState extends BaseState { // 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); + sumFocusNode.unfocus(); } - }); }); bonusFocusNode.addListener(() { setState(() { - - if (bonusFocusNode.hasFocus && sumFocusNode.hasFocus) { - sumFocusNode.unfocus(); - } - if (bonusFocusNode.hasFocus) { - scrollController.animateTo(pos, duration: new Duration(seconds: 1), curve: Curves.ease); + scrollController.animateTo(pos, + duration: new Duration(seconds: 1), curve: Curves.ease); } - }); }); } diff --git a/lib/screens/purchase_success.dart b/lib/screens/purchase_success.dart index ea99793..5f92556 100644 --- a/lib/screens/purchase_success.dart +++ b/lib/screens/purchase_success.dart @@ -2,6 +2,7 @@ import 'package:checker/base/base_state.dart'; import 'package:checker/common.dart'; import 'package:checker/consts.dart'; import 'package:checker/db.dart'; +import 'package:checker/screens/purchase_sum.dart'; import 'package:checker/strings.dart'; import 'package:flutter/material.dart'; @@ -133,7 +134,11 @@ class PurchaseSuccessScreenState extends BaseState { getScanButton() { String title = StringsLocalization.scan(); - return buildRaisedButton(title, () => Navigator.of(context).pop(token)); + return buildRaisedButton(title, + () => Navigator.pushAndRemoveUntil(context, + MaterialPageRoute(builder: (context) => + PurchaseSumScreen(widget.helper, widget.app, token)), + (route) => false)); } diff --git a/lib/screens/purchase_sum.dart b/lib/screens/purchase_sum.dart new file mode 100644 index 0000000..fb27dc9 --- /dev/null +++ b/lib/screens/purchase_sum.dart @@ -0,0 +1,204 @@ +import 'dart:convert'; +import 'dart:core'; + +import 'package:checker/base/base_screen.dart'; +import 'package:checker/base/base_state.dart'; +import 'package:checker/common.dart'; +import 'package:checker/consts.dart'; +import 'package:checker/db.dart'; +import 'package:checker/network.dart'; +import 'package:checker/resources.dart'; +import 'package:checker/screens/purchase.dart'; +import 'package:checker/strings.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:http/http.dart'; + +/// Экран ввода суммы покупки +class PurchaseSumScreen extends BaseScreen { + PurchaseSumScreen(helper, app, this.token) : super(helper, app); + + final String token; + + @override + State createState() => + new PurchaseSumScreenState(helper, app); +} + +class PurchaseSumScreenState extends BaseState { + TextEditingController _sumController = new TextEditingController(); + + bool isAutomaticallyImplyLeading() => false; + + PurchaseSumScreenState(SqliteHelper helper, String app) : super(helper, app); + + @override + void initState() { + super.initState(); + _subscribe(); + } + + @override + Widget build(BuildContext ctx) { + return getMainWidget(); + } + + @override + Widget getScreenContent() => Column( + children: [ + getHintLabel(), + getInputField(), + wrapButton( + _getScreenMargins(24.0), + getScanButton(context, StringsLocalization.scan(), + Resources.getPrimaryColor(app))) + ], + ); + + EdgeInsets _getScreenMargins(double top) { + double side = 42.0; + return new EdgeInsets.only(top: top, left: side, right: side); + } + + 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: () => _startScanner()), + decoration: new BoxDecoration( + border: new Border.all( + color: Resources.getButtonColor(app), width: 1.0), + borderRadius: new BorderRadius.all(new Radius.circular(4.0)))); + } + + void _startScanner() { + platform.invokeMethod('getEndpoint').then((url) { + platform.invokeMethod('getAppToken').then((appToken) { + Map args = StringsLocalization.strings; + args['token'] = widget.token; + args['url'] = url; + args['appToken'] = appToken; + args['localeCode'] = StringsLocalization.localeCode; + args['color'] = Resources.getPrimaryColor(app).value.toString(); + platform.invokeMethod('startScanner', args).then((result) { + _processResult(result); + }); + }); + }); + } + + @override + String getTitle() { + return StringsLocalization.sum(); + } + + @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: _sumController, + onSubmitted: (String text) { + setState(() { + _sumController.text = _parseSum(text); + }); + }, + textAlign: TextAlign.center); + } + + String _cleanupNumber(String text) { + String tmp = text + .replaceAll(' ', '') + .replaceAll('-', '') + .replaceAll(',', '.') + .replaceAll('..', '.'); + + while (tmp.indexOf('..') != -1) { + tmp = tmp.replaceAll('..', '.'); + } + return tmp; + } + + String _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); + } + + void _processResult(result) { + if (result is List) { + final String user = result[0] as String; + final String card = result[1] as String; + Navigator.of(context).push(new MaterialPageRoute(builder: (context) { + String sum = _parseSum(_sumController.text); + _sumController.text = ""; + return PurchaseScreen(helper, app, user, card, sum); + })); + } + } + + void _subscribe() async { + platform.setMethodCallHandler((MethodCall call) async { + print('Received method chanell call'); + if (call.method == 'findUser') { + try { + Response userResponse; + + switch (call.arguments[1]) { + case 'card': + userResponse = + await getUserByCard(call.arguments[0], widget.token); + break; + case 'phone': + userResponse = + await getUserByPhone(call.arguments[0], widget.token); + break; + } + + if (userResponse != null) { + print('I have user in method handler!'); + List users = json.decode(userResponse.body); + if (users.length > 0) { + return json.encode(users[0]); + } else { + throw new FlutterError("Users not found"); + } + } else { + throw new FlutterError("Users not found"); + } + } catch (error) { + print(error.toString()); + throw new FlutterError("Users not found"); + } + } else if (call.method == 'scanSuccess') { + _processResult(call.arguments); + } + }); + } +} diff --git a/lib/screens/splash.dart b/lib/screens/splash.dart index ba047a9..bde6283 100644 --- a/lib/screens/splash.dart +++ b/lib/screens/splash.dart @@ -11,6 +11,7 @@ import 'package:checker/resources.dart'; import 'package:checker/screens/faq.dart'; import 'package:checker/screens/finish_registration.dart'; import 'package:checker/screens/purchase.dart'; +import 'package:checker/screens/purchase_sum.dart'; import 'package:checker/screens/registration.dart'; import 'package:checker/screens/settings.dart'; import 'package:checker/strings.dart'; @@ -240,23 +241,13 @@ class _SplashScreenState extends BaseState { : json.encode(call.arguments[0]); print(userString); String card = call.arguments[1]; - showNextScreen(new PurchaseScreen(helper, app, userString, card)); + showNextScreen(new PurchaseScreen(helper, app, userString, card, null)); } }); - platform.invokeMethod('getEndpoint').then((url) { - platform.invokeMethod('getAppToken').then((appToken) { - Map args = StringsLocalization.strings; - args['token'] = token; - args['url'] = url; - args['appToken'] = appToken; - args['localeCode'] = StringsLocalization.localeCode; - args['color'] = Resources - .getPrimaryColor(app) - .value - .toString(); - platform.invokeMethod('startScanner', args); - }); - }); + Navigator.of(context).pushReplacement( + MaterialPageRoute(builder: (context) => + PurchaseSumScreen(helper, app, token)) + ); } } diff --git a/lib/strings.dart b/lib/strings.dart index 08ce633..ddae94b 100644 --- a/lib/strings.dart +++ b/lib/strings.dart @@ -1,22 +1,24 @@ import 'dart:async'; + +import 'package:flutter/services.dart'; import 'package:sprintf/sprintf.dart'; import 'package:xml/xml.dart' as xml; -import 'package:flutter/services.dart'; class StringsLocalization { - static String localeCode; static Map strings = new Map(); static Future load(String l) async { localeCode = l; - await rootBundle.loadString('assets/values-$localeCode/strings.xml').then((content) { + await rootBundle + .loadString('assets/values-$localeCode/strings.xml') + .then((content) { content.replaceAll('=\"', '=\\"'); content.replaceAll('\">', '\\">"'); var document = xml.parse(content); strings.clear(); document.findAllElements('string').forEach((node) { - strings[node.attributes[0].value] = node.text.toString(); + strings[node.attributes[0].value] = node.text.toString(); }); }); return strings; @@ -111,25 +113,45 @@ class StringsLocalization { return [nominative, singular, plural]; } + static _normalizeDouble(String val) => val.substring(0, val.length - 3); static String confirmPurchase(String val, int code) { - String trimmedVal = val.substring(0, val.length - 3); - return sprintf(strings['confirm_purchase'], [val, declineCurrency(int.parse(trimmedVal), code)]); + String trimmedVal = _normalizeDouble(val); + return sprintf(strings['confirm_purchase'], + [val, declineCurrency(int.parse(trimmedVal), code)]); } static String purchaseCompleted(String val, int code) { - String trimmedVal = val.substring(0, val.length - 3); - return sprintf(strings['purchase_complite'], [val, declineCurrency(int.parse(trimmedVal), code)]); + String trimmedVal = _normalizeDouble(val); + return sprintf(strings['purchase_complite'], + [val, declineCurrency(int.parse(trimmedVal), code)]); } + static String paymentCompleted(String val, int code) { String trimmedVal = val.substring(0, val.length - 3); - return sprintf(strings['payment_complite'], [val, declineCurrency(int.parse(trimmedVal), code)]); + return sprintf(strings['payment_complite'], + [val, declineCurrency(int.parse(trimmedVal), code)]); } + static String returnConfirmation(String sum, String date, int points) => - sprintf(strings['return_purchase_content'], [sum, date, points.toString()]); + sprintf( + strings['return_purchase_content'], [sum, date, points.toString()]); static String returnConfirmedContent(String sum, String date) => - sprintf(strings['return_confirmation_content'], [sum, date]); + sprintf(strings['return_confirmation_content'], [sum, date]); + + static String purchaseDetails( + String total, String discountTotal, String discount, int code) { + final String normTotal = _normalizeDouble(total); + final String normDiscountTotal = _normalizeDouble(discountTotal); + return sprintf(strings['purchase_details'], [ + total, + declineCurrency(int.parse(normTotal), code), + discountTotal, + declineCurrency(int.parse(normDiscountTotal), code), + discount + ]); + } static String registration() => strings['registration']; static String usage() => strings['usage']; @@ -142,7 +164,8 @@ class StringsLocalization { static String askChangeStore() => strings['ask_change_store']; static String yes() => strings['yes']; static String no() => strings['no']; - static String requestSentWaitActivation() => strings['request_sent_wait_activ']; + static String requestSentWaitActivation() => + strings['request_sent_wait_activ']; static String refreshActivationStatus() => strings['update_activ_status']; static String appActivated() => strings['app_activ']; static String completeRegistration() => strings['complite_activ']; @@ -218,7 +241,8 @@ class StringsLocalization { static String registrationGuide() => strings['registration_guide']; static String usageGuide() => strings['usage_guide']; static String commonGuide() => strings['common_guide'].replaceAll('\n', "\n"); - static String supportGuide(String phone, String url) => sprintf(strings['support_guide'], [phone, url]); + static String supportGuide(String phone, String url) => + sprintf(strings['support_guide'], [phone, url]); static String joys() => strings['joys']; static String joysMinus() => strings['joys_minus']; static String joysHint() => strings['joys_hint']; @@ -226,4 +250,6 @@ class StringsLocalization { static String returnLabel() => strings['returnLabel']; static String returnConfirmed() => strings['return_confirmation']; static String points() => strings['points']; - } + static String cancel() => strings['cancel']; + static String cancelDialog() => strings['purchase_cancellation']; +} diff --git a/pubspec.lock b/pubspec.lock index f06d8c4..fd4a58b 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1,5 +1,5 @@ # Generated by pub -# See https://www.dartlang.org/tools/pub/glossary#lockfile +# See https://dart.dev/tools/pub/glossary#lockfile packages: async: dependency: transitive @@ -61,7 +61,7 @@ packages: name: meta url: "https://pub.dartlang.org" source: hosted - version: "1.1.6" + version: "1.1.7" path: dependency: transitive description: @@ -166,5 +166,5 @@ packages: source: hosted version: "3.2.3" sdks: - dart: ">=2.0.0 <3.0.0" + dart: ">=2.2.2 <3.0.0" flutter: ">=0.1.4 <2.0.0"