#12267 change scan flow

This commit is contained in:
nikitateplyakov
2019-02-11 18:21:52 +08:00
parent d499269940
commit 5975252e8c
13 changed files with 334 additions and 83 deletions

View File

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

View File

@@ -1,5 +1,6 @@
import 'package:checker/base/base_screen.dart';
import 'package:checker/db.dart';
import 'package:checker/screens/purchase_sum.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'dart:convert';
@@ -14,13 +15,16 @@ import 'package:checker/network.dart';
import 'package:checker/base/base_state.dart';
import 'package:checker/screens/purchase_success.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<PurchaseScreen>(helper, app, user, card);
@@ -30,7 +34,7 @@ class PurchaseScreenState<T> extends BaseState<PurchaseScreen> {
/// Объект, помогающий вручную изменять введенный пользователем текст.
/// Используется для форматирования введенных пользователем данных
/// (удаляет запрещенные символы до их отображаения).
TextEditingController controller = new TextEditingController();
TextEditingController controller;
TextEditingController bonusController = new TextEditingController();
@@ -43,6 +47,7 @@ class PurchaseScreenState<T> extends BaseState<PurchaseScreen> {
@override
void initState() {
controller = new TextEditingController(text: widget.sum ?? "");
loading = true;
requestAsyncData(user);
buildFocusNode();
@@ -116,9 +121,9 @@ class PurchaseScreenState<T> extends BaseState<PurchaseScreen> {
widgetList.add(wrapButton(
getScreenMargins(24.0),
getScanButton(
getCancelScanButton(
context,
StringsLocalization.scan(),
StringsLocalization.cancel(),
Resources.getPrimaryColor(app)
)
));
@@ -185,12 +190,12 @@ class PurchaseScreenState<T> extends BaseState<PurchaseScreen> {
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()),
onPressed: () => restartFlow()),
decoration: new BoxDecoration(
border: new Border.all(
color: Resources.getButtonColor(app), width: 1.0),
@@ -249,8 +254,8 @@ class PurchaseScreenState<T> extends BaseState<PurchaseScreen> {
loading = false;
this.coupons = coupons['results'];
this.loyalityType = loyality['type'];
setBonuses(loyality, showBonus);
});
setBonuses(loyality, showBonus);
}
}
@@ -293,16 +298,26 @@ class PurchaseScreenState<T> extends BaseState<PurchaseScreen> {
}
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(
builder: (_) =>
new AlertDialog(
title: new Text(StringsLocalization.confirmation()),
content:
new Text(
StringsLocalization.confirmPurchase(val, currency)
content: Text(
StringsLocalization.purchaseDetails(totalAmount,
totalDiscount, discount, currency)
),
actions: <Widget>[
new FlatButton(
@@ -315,13 +330,42 @@ class PurchaseScreenState<T> extends BaseState<PurchaseScreen> {
child: new Text(StringsLocalization.yes()),
onPressed: () {
Navigator.of(context).pop();
purchase(val);
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<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) {
showDialog(
context: context,
@@ -339,13 +383,13 @@ class PurchaseScreenState<T> extends BaseState<PurchaseScreen> {
);
}
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();
@@ -361,10 +405,13 @@ class PurchaseScreenState<T> extends BaseState<PurchaseScreen> {
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;
}
@@ -392,32 +439,7 @@ class PurchaseScreenState<T> extends BaseState<PurchaseScreen> {
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<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);
});
});
callback(purchase, sumTotal, token);
}
}
}
@@ -426,24 +448,47 @@ class PurchaseScreenState<T> extends BaseState<PurchaseScreen> {
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)}%';
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: <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();
@@ -456,25 +501,14 @@ class PurchaseScreenState<T> extends BaseState<PurchaseScreen> {
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);
}

View File

@@ -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';
@@ -135,7 +136,11 @@ class PurchaseSuccessScreenState<T> extends BaseState<PurchaseSuccessScreen> {
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));
}

View File

@@ -0,0 +1,202 @@
import 'dart:convert';
import 'package:checker/base/base_screen.dart';
import 'package:checker/db.dart';
import 'package:checker/screens/purchase.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:http/http.dart';
import 'dart:core';
import 'package:checker/resources.dart';
import 'package:checker/strings.dart';
import 'package:checker/common.dart';
import 'package:checker/consts.dart';
import 'package:checker/base/base_state.dart';
import 'package:checker/network.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 {
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/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';
@@ -241,23 +242,13 @@ class _SplashScreenState extends BaseState<SplashScreen> {
: 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<String, String> 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))
);
}
}