13450 merge 12267_new_flow and update to new flutter version

This commit is contained in:
nikitateplyakov
2019-11-20 16:37:36 +03:00
16 changed files with 490 additions and 226 deletions

View File

@@ -211,5 +211,6 @@ dependencies {
} }
repositories { repositories {
google()
mavenCentral() mavenCentral()
} }

View File

@@ -38,6 +38,7 @@ public class MainActivity extends FlutterActivity {
private MethodChannel mChannel; private MethodChannel mChannel;
private Map mScannerArgs; private Map mScannerArgs;
private Result scannerResult;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
@@ -59,6 +60,7 @@ public class MainActivity extends FlutterActivity {
result.success(BuildConfig.currency); result.success(BuildConfig.currency);
break; break;
case "startScanner": case "startScanner":
scannerResult = result;
startScannerActivity(call); startScannerActivity(call);
break; break;
case "isOnline": case "isOnline":
@@ -169,7 +171,7 @@ public class MainActivity extends FlutterActivity {
protected void onActivityResult(int requestCode, int resultCode, Intent data) { protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == START_SCANNER_REQUEST_CODE) { if (requestCode == START_SCANNER_REQUEST_CODE) {
if (resultCode == RESULT_CANCELED) { if (resultCode == RESULT_CANCELED) {
finish(); scannerResult.error("Scanning is cancelled", null, null);
} else if (resultCode == RESULT_OK) { } else if (resultCode == RESULT_OK) {
if (data != null && data.getExtras() != null) { if (data != null && data.getExtras() != null) {
String user = data.getExtras().getString("user", null); String user = data.getExtras().getString("user", null);
@@ -178,7 +180,9 @@ public class MainActivity extends FlutterActivity {
ArrayList<String> args = new ArrayList<>(2); ArrayList<String> args = new ArrayList<>(2);
args.add(user); args.add(user);
args.add(card); args.add(card);
mChannel.invokeMethod("purchase", args); if(scannerResult != null) {
scannerResult.success(args);
}
} else { } else {
String menuItem = data.getExtras().getString("item", null); String menuItem = data.getExtras().getString("item", null);
if (menuItem != null) { if (menuItem != null) {

View File

@@ -13,8 +13,8 @@ buildscript {
allprojects { allprojects {
repositories { repositories {
jcenter()
google() google()
jcenter()
} }
} }

View File

@@ -162,4 +162,7 @@ You bonus balance will be changed and you will have %s bonus points.</string>
<string name="return_confirmation">Return confirmed</string> <string name="return_confirmation">Return confirmed</string>
<string name="return_confirmation_content">Purchase, which cost %s on %s is returned.</string> <string name="return_confirmation_content">Purchase, which cost %s on %s is returned.</string>
<string name="points">Points</string> <string name="points">Points</string>
<string name="cancel">Cancel</string>
<string name="purchase_cancellation">Are you sure you want to cancel this purchase?</string>
<string name="purchase_details">Total amount: %s %s, with discount of %s %s (%s%%)</string>
</resources> </resources>

View File

@@ -158,4 +158,7 @@ Se cambiará su saldo de bonificación y tendrá %s puntos de bonificación.</st
<string name="return_confirmation">Regreso confirmado</string> <string name="return_confirmation">Regreso confirmado</string>
<string name="return_confirmation_content">Compra, cuyo costo %s en %s se devuelve.</string> <string name="return_confirmation_content">Compra, cuyo costo %s en %s se devuelve.</string>
<string name="points">Puntos</string> <string name="points">Puntos</string>
<string name="cancel">Cancelar</string>
<string name="purchase_cancellation">¿Seguro que quieres cancelar esta compra?</string>
<string name="purchase_details">Cantidad total: %s %s, con descuento de %s %s (%s%%)</string>
</resources> </resources>

View File

@@ -161,4 +161,7 @@
<string name="return_confirmation">Возврат подтвержден</string> <string name="return_confirmation">Возврат подтвержден</string>
<string name="return_confirmation_content">Покупка на сумму %s от %s возвращена.</string> <string name="return_confirmation_content">Покупка на сумму %s от %s возвращена.</string>
<string name="points">Баллы</string> <string name="points">Баллы</string>
<string name="cancel">Отмена</string>
<string name="purchase_cancellation">Вы уверены что хотите отменить покупку?</string>
<string name="purchase_details">Итого покупка: %s %s, Со скидкой %s %s (%s%%)</string>
</resources> </resources>

View File

@@ -163,4 +163,7 @@
<string name="return_confirmation">Повернення підтверджено</string> <string name="return_confirmation">Повернення підтверджено</string>
<string name="return_confirmation_content">Повертається придбання, вартість якого %s на %s.</string> <string name="return_confirmation_content">Повертається придбання, вартість якого %s на %s.</string>
<string name="points">Бонусні бали</string> <string name="points">Бонусні бали</string>
<string name="cancel">Скасувати</string>
<string name="purchase_cancellation">Дійсно скасувати цю покупку?</string>
<string name="purchase_details">Загальна кількість: %s %s, зі знижкою %s %s (%s%%)</string>
</resources> </resources>

View File

@@ -12,9 +12,7 @@
FlutterMethodChannel* platformChannel = [FlutterMethodChannel FlutterMethodChannel* platformChannel = [FlutterMethodChannel
methodChannelWithName:@"com.dinect.checker/instance_id" methodChannelWithName:@"com.dinect.checker/instance_id"
binaryMessenger:controller]; binaryMessenger:controller.binaryMessenger];
__weak FlutterMethodChannel* weekPlatformChannel = platformChannel;
NSString *bundleIdentifier = [[NSBundle mainBundle] bundleIdentifier]; NSString *bundleIdentifier = [[NSBundle mainBundle] bundleIdentifier];
@@ -116,7 +114,7 @@
result(buildSettings[@"currency"]); result(buildSettings[@"currency"]);
} else if ([@"startScanner" isEqualToString:call.method]) { } else if ([@"startScanner" isEqualToString:call.method]) {
ScannerViewController *modalViewController = [[ScannerViewController alloc] initWithStrings:call.arguments]; ScannerViewController *modalViewController = [[ScannerViewController alloc] initWithStrings:call.arguments];
modalViewController.platformChannel = weekPlatformChannel; modalViewController.platformChannel = platformChannel;
[controller presentViewController:modalViewController animated:YES completion:nil]; [controller presentViewController:modalViewController animated:YES completion:nil];
} else if ([@"isOnline" isEqualToString:call.method]) { } else if ([@"isOnline" isEqualToString:call.method]) {
result(@YES); result(@YES);

View File

@@ -272,7 +272,7 @@ import ZXingObjC
} else { } else {
print("Result is not nil (ios code)"); print("Result is not nil (ios code)");
self.dismiss(animated: true) { self.dismiss(animated: true) {
self.platformChannel?.invokeMethod("purchase", arguments: [result, str]) self.platformChannel?.invokeMethod("scanSuccess", arguments: [result!, str])
} }
} }
}) })

View File

@@ -1,5 +1,4 @@
import 'dart:convert'; import 'dart:convert';
import 'dart:async';
import 'package:checker/base/base_screen.dart'; import 'package:checker/base/base_screen.dart';
import 'package:checker/base/base_state.dart'; import 'package:checker/base/base_state.dart';

View File

@@ -1,52 +1,59 @@
import 'package:checker/base/base_screen.dart'; import 'dart:async';
import 'package:checker/db.dart';
import 'package:checker/screens/return.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'dart:convert'; import 'dart:convert';
import 'dart:core'; import 'dart:core';
import 'dart:async';
import 'package:checker/resources.dart'; import 'package:checker/base/base_screen.dart';
import 'package:checker/strings.dart'; import 'package:checker/base/base_state.dart';
import 'package:checker/common.dart'; import 'package:checker/common.dart';
import 'package:checker/consts.dart'; import 'package:checker/consts.dart';
import 'package:checker/db.dart';
import 'package:checker/network.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_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 { class PurchaseScreen extends BaseScreen {
PurchaseScreen(helper, app, this.user, this.card, this.sum)
PurchaseScreen(helper, app, this.user, this.card) : super(helper, app); : super(helper, app);
final String user; final String user;
final String card; final String card;
final String sum;
@override @override
State createState() => new PurchaseScreenState<PurchaseScreen>(helper, app, user, card); State createState() =>
new PurchaseScreenState<PurchaseScreen>(helper, app, user, card);
} }
class PurchaseScreenState<T> extends BaseState<PurchaseScreen> { class PurchaseScreenState<T> extends BaseState<PurchaseScreen> {
/// Объект, помогающий вручную изменять введенный пользователем текст. /// Объект, помогающий вручную изменять введенный пользователем текст.
/// Используется для форматирования введенных пользователем данных /// Используется для форматирования введенных пользователем данных
/// (удаляет запрещенные символы до их отображаения). /// (удаляет запрещенные символы до их отображаения).
TextEditingController controller = new TextEditingController(); TextEditingController controller;
TextEditingController bonusController = new TextEditingController(); TextEditingController bonusController = new TextEditingController();
bool isAutomaticallyImplyLeading() => false; 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.user = json.decode(userString);
this.card = card; this.card = card;
} }
@override @override
void initState() { void initState() {
controller = new TextEditingController(text: widget.sum ?? "");
loading = true; loading = true;
requestAsyncData(user); requestAsyncData(user);
loading = false;
buildFocusNode(); buildFocusNode();
scrollController = new ScrollController(); scrollController = new ScrollController();
super.initState(); super.initState();
@@ -70,21 +77,22 @@ class PurchaseScreenState<T> extends BaseState<PurchaseScreen> {
@override @override
Widget getScreenContent() { Widget getScreenContent() {
List<Widget> widgetList = []; List<Widget> widgetList = [];
widgetList.add(getValueWithDescription( widgetList.add(getValueWithDescription(StringsLocalization.buyer(),
StringsLocalization.buyer(), user['first_name'] == null ? '' : user['first_name']));
user['first_name'] == null ? '' : user['first_name']
));
widgetList.add(getValueWithDescription(StringsLocalization.card(), card)); widgetList.add(getValueWithDescription(StringsLocalization.card(), card));
if ((app != 'crypto')) { if (app != 'crypto') {
widgetList.add(getValueWithDescription(StringsLocalization.reward(), loyalty)); widgetList
.add(getValueWithDescription(StringsLocalization.reward(), loyalty));
if (bonus.length > 0) { if (bonus.length > 0) {
widgetList.add(getValueWithDescription(StringsLocalization.bonus(), bonus)); widgetList
.add(getValueWithDescription(StringsLocalization.bonus(), bonus));
} }
} }
widgetList.add(getHintLabel()); widgetList.add(getHintLabel());
widgetList.add(getInputField()); // Нельзя добавить еще одно поле таким же способом widgetList.add(
getInputField()); // Нельзя добавить еще одно поле таким же способом
if ((app != 'autobonus') && (app != 'crypto')) { if ((app != 'autobonus') && (app != 'crypto')) {
if (this.loyalityType == 'bonus') { if (this.loyalityType == 'bonus') {
@@ -94,16 +102,12 @@ class PurchaseScreenState<T> extends BaseState<PurchaseScreen> {
if (this.coupons.length > 0) { if (this.coupons.length > 0) {
widgetList.add(getItemTitle(StringsLocalization.selectCoupons())); widgetList.add(getItemTitle(StringsLocalization.selectCoupons()));
this.coupons.forEach((couponItem) { this.coupons.forEach((couponItem) {
widgetList.add(getItemToggleSwitch( widgetList.add(getItemToggleSwitch(couponItem['offer_name'],
couponItem['offer_name'], couponItem['coupon_condition'], couponItem['isSet'], (val) {
couponItem['coupon_condition'], setState(() {
couponItem['isSet'], couponItem['isSet'] = val;
(val) { });
setState(() { }));
couponItem['isSet'] = val;
});
}
));
}); });
} }
} }
@@ -111,16 +115,14 @@ class PurchaseScreenState<T> extends BaseState<PurchaseScreen> {
widgetList.add(wrapButton(getScreenMargins(36.0), getCompleteButton())); widgetList.add(wrapButton(getScreenMargins(36.0), getCompleteButton()));
widgetList.add(wrapButton( widgetList.add(wrapButton(
getScreenMargins(24.0), getScreenMargins(24.0),
getScanButton( getCancelScanButton(context, StringsLocalization.cancel(),
context, Resources.getPrimaryColor(app))));
StringsLocalization.scan(),
Resources.getPrimaryColor(app)
)
));
if (this.coupons.length == 0) { 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); listView = new ListView(children: widgetList, controller: scrollController);
@@ -131,8 +133,10 @@ class PurchaseScreenState<T> extends BaseState<PurchaseScreen> {
void openReturnScreen() { void openReturnScreen() {
Future.delayed(const Duration(milliseconds: 200), () { Future.delayed(const Duration(milliseconds: 200), () {
Route route = MaterialPageRoute<String>(builder: (BuildContext context) => Route route = MaterialPageRoute<String>(
ReturnScreen(helper, app, user['id'], user['bonus']), fullscreenDialog: true); builder: (BuildContext context) =>
ReturnScreen(helper, app, user['id'], user['bonus']),
fullscreenDialog: true);
Navigator.push(context, route); Navigator.push(context, route);
}); });
} }
@@ -141,9 +145,8 @@ class PurchaseScreenState<T> extends BaseState<PurchaseScreen> {
var bonusTextField = new TextField( var bonusTextField = new TextField(
keyboardType: TextInputType.number, keyboardType: TextInputType.number,
decoration: new InputDecoration.collapsed( decoration: new InputDecoration.collapsed(
hintText: StringsLocalization.bonusHint(), hintText: StringsLocalization.bonusHint(),
hintStyle: new TextStyle(color: greyTextColor, fontSize: 16.0) hintStyle: new TextStyle(color: greyTextColor, fontSize: 16.0)),
),
focusNode: bonusFocusNode, focusNode: bonusFocusNode,
controller: bonusController, controller: bonusController,
onSubmitted: (String text) { onSubmitted: (String text) {
@@ -157,26 +160,21 @@ class PurchaseScreenState<T> extends BaseState<PurchaseScreen> {
return new Column( return new Column(
children: <Widget>[ children: <Widget>[
new Container( new Container(
margin: new EdgeInsets.only( margin: new EdgeInsets.only(
left: verticalMargin, left: verticalMargin,
right: verticalMargin, right: verticalMargin,
top: verticalMargin top: verticalMargin),
), padding: getInputFieldContainerPadding(),
padding: getInputFieldContainerPadding(), decoration: getInputFieldContainerDecoration(),
decoration: getInputFieldContainerDecoration(), child: bonusTextField),
child: bonusTextField
),
new Container( new Container(
margin: new EdgeInsets.only( margin: new EdgeInsets.only(top: 5.0),
top: 5.0 child: new Text(
), StringsLocalization.bonusExplanation(),
child: new Text( textAlign: TextAlign.center,
StringsLocalization.bonusExplanation(), overflow: TextOverflow.ellipsis,
textAlign: TextAlign.center, style: new TextStyle(color: greyTextColor, fontSize: 11.0),
overflow: TextOverflow.ellipsis, ))
style: new TextStyle(color: greyTextColor, fontSize: 11.0),
)
)
], ],
); );
} }
@@ -188,19 +186,20 @@ class PurchaseScreenState<T> extends BaseState<PurchaseScreen> {
getCompleteButton() { getCompleteButton() {
return buildRaisedButton( 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( return new Container(
height: buttonHeight, height: buttonHeight,
child: new FlatButton( child: new FlatButton(
child: new Text(title, style: new TextStyle(color: textColor)), child: new Text(title, style: new TextStyle(color: textColor)),
onPressed: () => restartScanner()), onPressed: () => restartFlow()),
decoration: new BoxDecoration( decoration: new BoxDecoration(
border: new Border.all( border: new Border.all(
color: Resources.getButtonColor(app), width: 1.0), color: Resources.getButtonColor(app), width: 1.0),
borderRadius: new BorderRadius.all(new Radius.circular(4.0)))); borderRadius: new BorderRadius.all(new Radius.circular(4.0))));
} }
@override @override
@@ -216,18 +215,18 @@ class PurchaseScreenState<T> extends BaseState<PurchaseScreen> {
@override @override
getTextWidget() { getTextWidget() {
return new TextField( return new TextField(
keyboardType: TextInputType.number, keyboardType: TextInputType.number,
decoration: new InputDecoration.collapsed( decoration: new InputDecoration.collapsed(
hintText: getHintString(), hintText: getHintString(),
hintStyle: new TextStyle(color: greyTextColor, fontSize: 16.0)), hintStyle: new TextStyle(color: greyTextColor, fontSize: 16.0)),
controller: controller, controller: controller,
focusNode: sumFocusNode, focusNode: sumFocusNode,
onSubmitted: (String text) { onSubmitted: (String text) {
setState(() { setState(() {
controller.text = _parseSum(text); controller.text = _parseSum(text);
}); });
}, },
textAlign: TextAlign.center); textAlign: TextAlign.center);
} }
requestAsyncData(Map user) async { requestAsyncData(Map user) async {
@@ -239,10 +238,8 @@ class PurchaseScreenState<T> extends BaseState<PurchaseScreen> {
var token = await helper.getToken(); var token = await helper.getToken();
response = await getLoyaltyRequest(user['loyalty_url'], token); response = await getLoyaltyRequest(user['loyalty_url'], token);
couponResponse = await getCouponsRequest( couponResponse = await getCouponsRequest(
user['coupons_url'] + '?status=ACTIVE', user['coupons_url'] + '?status=ACTIVE', token);
token } catch (error) {
);
} catch(error) {
print(error.toString()); print(error.toString());
} }
Map loyality = json.decode(response.body); Map loyality = json.decode(response.body);
@@ -263,10 +260,10 @@ class PurchaseScreenState<T> extends BaseState<PurchaseScreen> {
// TODO: Переделать, если потребуется // TODO: Переделать, если потребуется
String _cleanupNumber(String text) { String _cleanupNumber(String text) {
String tmp = text String tmp = text
.replaceAll(' ', '') .replaceAll(' ', '')
.replaceAll('-', '') .replaceAll('-', '')
.replaceAll(',', '.') .replaceAll(',', '.')
.replaceAll('..', '.'); .replaceAll('..', '.');
while (tmp.indexOf('..') != -1) { while (tmp.indexOf('..') != -1) {
tmp = tmp.replaceAll('..', '.'); tmp = tmp.replaceAll('..', '.');
@@ -299,59 +296,94 @@ class PurchaseScreenState<T> extends BaseState<PurchaseScreen> {
} }
onPurchaseClick() { onPurchaseClick() {
String val = _parseSum(controller.text);
purchase(val, false, _showPrecalculatedValues);
}
void _showPrecalculatedValues(Map response, String sumTotal, String token) {
String val = _parseSum(controller.text); String val = _parseSum(controller.text);
helper.getCurrency().then((currency) { helper.getCurrency().then((currency) {
print(currency.toString()); 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( showDialog(
context: context, context: context,
builder: (_) => new AlertDialog( builder: (_) => new AlertDialog(
title: new Text(StringsLocalization.confirmation()), title: new Text(StringsLocalization.confirmation()),
content: content: Text(StringsLocalization.purchaseDetails(
new Text( totalAmount, totalDiscount, discount, currency)),
StringsLocalization.confirmPurchase(val, currency) actions: <Widget>[
), new FlatButton(
actions: <Widget>[ child: new Text(StringsLocalization.no()),
new FlatButton( onPressed: () {
child: new Text(StringsLocalization.no()), Navigator.of(context).pop();
onPressed: () { },
Navigator.of(context).pop(); ),
}, new FlatButton(
), child: new Text(StringsLocalization.yes()),
new FlatButton( onPressed: () {
child: new Text(StringsLocalization.yes()), Navigator.of(context).pop();
onPressed: () { purchase(val, true, _paymentConfirmed);
Navigator.of(context).pop(); },
purchase(val); )
}, ]));
) });
])); }
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<String>(
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) { apiErrorAlert(String errorText) {
showDialog( showDialog(
context: context, context: context,
builder: (_) => new AlertDialog( builder: (_) =>
content: new Text(errorText), new AlertDialog(content: new Text(errorText), actions: <Widget>[
actions: <Widget>[ new FlatButton(
new FlatButton( child: new Text('Ok'),
child: new Text('Ok'), onPressed: () {
onPressed: () { Navigator.of(context).pop();
Navigator.of(context).pop(); },
}, )
) ]));
]
)
);
} }
purchase(String sumTotal) async { purchase(
String sumTotal, bool commit, PurchaseResponseCallback callback) async {
setState(() { setState(() {
loading = true; loading = true;
}); });
if (await platform.invokeMethod('isOnline')) { if (await platform.invokeMethod('isOnline')) {
if (!purchaseInProgress) { if (!purchaseInProgress) {
purchaseInProgress = true; purchaseInProgress = commit;
String token = await helper.getToken(); String token = await helper.getToken();
var result = await helper.getMerchantID(); var result = await helper.getMerchantID();
@@ -367,10 +399,13 @@ class PurchaseScreenState<T> extends BaseState<PurchaseScreen> {
var body = { var body = {
'doc_id': result, 'doc_id': result,
'curr_iso_code': currency.toString(), 'curr_iso_code': currency.toString(),
'commit': 'true',
'sum_total': sumTotal, 'sum_total': sumTotal,
}; };
if (commit) {
body['commit'] = 'true';
}
if (bonusController.text.length > 0) { if (bonusController.text.length > 0) {
body['bonus_payment'] = bonusController.text; body['bonus_payment'] = bonusController.text;
} }
@@ -383,9 +418,10 @@ class PurchaseScreenState<T> extends BaseState<PurchaseScreen> {
Map purchase; Map purchase;
try { try {
purchaseResponse = await getPurchaseRequest(user['purchases_url'], body, token); purchaseResponse =
await getPurchaseRequest(user['purchases_url'], body, token);
purchase = json.decode(purchaseResponse.body); purchase = json.decode(purchaseResponse.body);
} catch(error) { } catch (error) {
purchaseInProgress = false; purchaseInProgress = false;
print(error.toString()); print(error.toString());
} }
@@ -398,32 +434,7 @@ class PurchaseScreenState<T> extends BaseState<PurchaseScreen> {
purchaseInProgress = false; purchaseInProgress = false;
apiErrorAlert(errors[0]); apiErrorAlert(errors[0]);
} else { } else {
var couponsResponse; callback(purchase, sumTotal, token);
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<String>(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);
});
});
} }
} }
} }
@@ -432,24 +443,49 @@ class PurchaseScreenState<T> extends BaseState<PurchaseScreen> {
void setBonuses(Map bonuses, bool showBonus) { void setBonuses(Map bonuses, bool showBonus) {
print('loyalityType ' + this.loyalityType); print('loyalityType ' + this.loyalityType);
if (bonuses['type'] == 'amount') { if (bonuses['type'] == 'amount') {
this.loyalty = '${user['discount']}%'; setState(() => this.loyalty = '${user['discount']}%');
} else { } else {
double loyaltyVal = (double.parse(bonuses['amount_to_bonus'][1]) / double loyaltyVal = (double.parse(bonuses['amount_to_bonus'][1]) /
bonuses['amount_to_bonus'][0]) * 100; bonuses['amount_to_bonus'][0]) *
this.loyalty = '${loyaltyVal.toStringAsFixed(0)}%'; 100;
setState(() => this.loyalty = '${loyaltyVal.toStringAsFixed(0)}%');
} }
if (showBonus && (this.loyalityType == 'bonus')) { if (showBonus && (this.loyalityType == 'bonus')) {
this.bonus = '${user['bonus']}'; setState(() => this.bonus = '${user['bonus']}');
} }
print('loyalty ' + this.loyalty); print('loyalty ' + this.loyalty);
print('bonus ' + this.bonus); print('bonus ' + this.bonus);
} }
restartScanner() { restartFlow() {
helper.getToken().then((token) { showDialog(
Navigator.of(context).pop(token); context: context,
}); builder: (context) => AlertDialog(
content: Text(StringsLocalization.cancelDialog()),
actions: <Widget>[
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(); FocusNode bonusFocusNode = new FocusNode();
@@ -457,34 +493,22 @@ class PurchaseScreenState<T> extends BaseState<PurchaseScreen> {
// TODO: Удалить дублирующийся код. // TODO: Удалить дублирующийся код.
void buildFocusNode() { void buildFocusNode() {
var pos = this.coupons.length > 0 ? 150.0 : 100.0; var pos = this.coupons.length > 0 ? 150.0 : 100.0;
sumFocusNode.addListener(() { sumFocusNode.addListener(() {
setState(() { setState(() {
if (sumFocusNode.hasFocus && bonusFocusNode.hasFocus) {
bonusFocusNode.unfocus();
}
if (sumFocusNode.hasFocus) { if (sumFocusNode.hasFocus) {
scrollController.animateTo(pos, duration: new Duration(seconds: 1), curve: Curves.ease); sumFocusNode.unfocus();
} }
}); });
}); });
bonusFocusNode.addListener(() { bonusFocusNode.addListener(() {
setState(() { setState(() {
if (bonusFocusNode.hasFocus && sumFocusNode.hasFocus) {
sumFocusNode.unfocus();
}
if (bonusFocusNode.hasFocus) { 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);
} }
}); });
}); });
} }

View File

@@ -2,6 +2,7 @@ import 'package:checker/base/base_state.dart';
import 'package:checker/common.dart'; import 'package:checker/common.dart';
import 'package:checker/consts.dart'; import 'package:checker/consts.dart';
import 'package:checker/db.dart'; import 'package:checker/db.dart';
import 'package:checker/screens/purchase_sum.dart';
import 'package:checker/strings.dart'; import 'package:checker/strings.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@@ -133,7 +134,11 @@ class PurchaseSuccessScreenState<T> extends BaseState<PurchaseSuccessScreen> {
getScanButton() { getScanButton() {
String title = StringsLocalization.scan(); 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));
} }

View File

@@ -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<PurchaseSumScreen>(helper, app);
}
class PurchaseSumScreenState<T> extends BaseState<PurchaseSumScreen> {
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: <Widget>[
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<String, String> 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<dynamic> 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);
}
});
}
}

View File

@@ -11,6 +11,7 @@ import 'package:checker/resources.dart';
import 'package:checker/screens/faq.dart'; import 'package:checker/screens/faq.dart';
import 'package:checker/screens/finish_registration.dart'; import 'package:checker/screens/finish_registration.dart';
import 'package:checker/screens/purchase.dart'; import 'package:checker/screens/purchase.dart';
import 'package:checker/screens/purchase_sum.dart';
import 'package:checker/screens/registration.dart'; import 'package:checker/screens/registration.dart';
import 'package:checker/screens/settings.dart'; import 'package:checker/screens/settings.dart';
import 'package:checker/strings.dart'; import 'package:checker/strings.dart';
@@ -240,23 +241,13 @@ class _SplashScreenState extends BaseState<SplashScreen> {
: json.encode(call.arguments[0]); : json.encode(call.arguments[0]);
print(userString); print(userString);
String card = call.arguments[1]; 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) { Navigator.of(context).pushReplacement(
platform.invokeMethod('getAppToken').then((appToken) { MaterialPageRoute(builder: (context) =>
Map<String, String> args = StringsLocalization.strings; PurchaseSumScreen(helper, app, token))
args['token'] = token; );
args['url'] = url;
args['appToken'] = appToken;
args['localeCode'] = StringsLocalization.localeCode;
args['color'] = Resources
.getPrimaryColor(app)
.value
.toString();
platform.invokeMethod('startScanner', args);
});
});
} }
} }

View File

@@ -1,22 +1,24 @@
import 'dart:async'; import 'dart:async';
import 'package:flutter/services.dart';
import 'package:sprintf/sprintf.dart'; import 'package:sprintf/sprintf.dart';
import 'package:xml/xml.dart' as xml; import 'package:xml/xml.dart' as xml;
import 'package:flutter/services.dart';
class StringsLocalization { class StringsLocalization {
static String localeCode; static String localeCode;
static Map<String, String> strings = new Map<String, String>(); static Map<String, String> strings = new Map<String, String>();
static Future load(String l) async { static Future load(String l) async {
localeCode = l; 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('=\"', '=\\"');
content.replaceAll('\">', '\\">"'); content.replaceAll('\">', '\\">"');
var document = xml.parse(content); var document = xml.parse(content);
strings.clear(); strings.clear();
document.findAllElements('string').forEach((node) { document.findAllElements('string').forEach((node) {
strings[node.attributes[0].value] = node.text.toString(); strings[node.attributes[0].value] = node.text.toString();
}); });
}); });
return strings; return strings;
@@ -111,25 +113,45 @@ class StringsLocalization {
return [nominative, singular, plural]; return [nominative, singular, plural];
} }
static _normalizeDouble(String val) => val.substring(0, val.length - 3);
static String confirmPurchase(String val, int code) { static String confirmPurchase(String val, int code) {
String trimmedVal = val.substring(0, val.length - 3); String trimmedVal = _normalizeDouble(val);
return sprintf(strings['confirm_purchase'], [val, declineCurrency(int.parse(trimmedVal), code)]); return sprintf(strings['confirm_purchase'],
[val, declineCurrency(int.parse(trimmedVal), code)]);
} }
static String purchaseCompleted(String val, int code) { static String purchaseCompleted(String val, int code) {
String trimmedVal = val.substring(0, val.length - 3); String trimmedVal = _normalizeDouble(val);
return sprintf(strings['purchase_complite'], [val, declineCurrency(int.parse(trimmedVal), code)]); return sprintf(strings['purchase_complite'],
[val, declineCurrency(int.parse(trimmedVal), code)]);
} }
static String paymentCompleted(String val, int code) { static String paymentCompleted(String val, int code) {
String trimmedVal = val.substring(0, val.length - 3); 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) => 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) => 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 registration() => strings['registration'];
static String usage() => strings['usage']; static String usage() => strings['usage'];
@@ -142,7 +164,8 @@ class StringsLocalization {
static String askChangeStore() => strings['ask_change_store']; static String askChangeStore() => strings['ask_change_store'];
static String yes() => strings['yes']; static String yes() => strings['yes'];
static String no() => strings['no']; 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 refreshActivationStatus() => strings['update_activ_status'];
static String appActivated() => strings['app_activ']; static String appActivated() => strings['app_activ'];
static String completeRegistration() => strings['complite_activ']; static String completeRegistration() => strings['complite_activ'];
@@ -218,7 +241,8 @@ class StringsLocalization {
static String registrationGuide() => strings['registration_guide']; static String registrationGuide() => strings['registration_guide'];
static String usageGuide() => strings['usage_guide']; static String usageGuide() => strings['usage_guide'];
static String commonGuide() => strings['common_guide'].replaceAll('\n', "\n"); 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 joys() => strings['joys'];
static String joysMinus() => strings['joys_minus']; static String joysMinus() => strings['joys_minus'];
static String joysHint() => strings['joys_hint']; static String joysHint() => strings['joys_hint'];
@@ -226,4 +250,6 @@ class StringsLocalization {
static String returnLabel() => strings['returnLabel']; static String returnLabel() => strings['returnLabel'];
static String returnConfirmed() => strings['return_confirmation']; static String returnConfirmed() => strings['return_confirmation'];
static String points() => strings['points']; static String points() => strings['points'];
} static String cancel() => strings['cancel'];
static String cancelDialog() => strings['purchase_cancellation'];
}

View File

@@ -1,5 +1,5 @@
# Generated by pub # Generated by pub
# See https://www.dartlang.org/tools/pub/glossary#lockfile # See https://dart.dev/tools/pub/glossary#lockfile
packages: packages:
async: async:
dependency: transitive dependency: transitive
@@ -61,7 +61,7 @@ packages:
name: meta name: meta
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.1.6" version: "1.1.7"
path: path:
dependency: transitive dependency: transitive
description: description:
@@ -166,5 +166,5 @@ packages:
source: hosted source: hosted
version: "3.2.3" version: "3.2.3"
sdks: sdks:
dart: ">=2.0.0 <3.0.0" dart: ">=2.2.2 <3.0.0"
flutter: ">=0.1.4 <2.0.0" flutter: ">=0.1.4 <2.0.0"