From 0659198a1711c312d69aaf6679ab093609be5c33 Mon Sep 17 00:00:00 2001 From: nikitateplyakov Date: Wed, 6 Mar 2019 23:42:10 +0800 Subject: [PATCH] return purchases screen --- assets/values-en/strings.xml | 8 ++ assets/values-ru/strings.xml | 8 ++ lib/base/base_state.dart | 18 +++- lib/network.dart | 30 ++++++ lib/screens/purchase.dart | 11 +++ lib/screens/return.dart | 186 +++++++++++++++++++++++++++++++++++ lib/strings.dart | 8 ++ pubspec.lock | 7 ++ pubspec.yaml | 1 + 9 files changed, 275 insertions(+), 2 deletions(-) create mode 100644 lib/screens/return.dart diff --git a/assets/values-en/strings.xml b/assets/values-en/strings.xml index 0015bb0..b2b3ee7 100644 --- a/assets/values-en/strings.xml +++ b/assets/values-en/strings.xml @@ -154,4 +154,12 @@ Set on switch on Camera Go back to %s. Open settings + Return + Are you sure you want to return purchase, which cost %s on %s? + +You cannot undo this. +You bonus balance will be changed and you will have %s bonus points. + Return confirmed + Purchase, which cost %s on %s is returned. + Points diff --git a/assets/values-ru/strings.xml b/assets/values-ru/strings.xml index d51ed37..b84f307 100644 --- a/assets/values-ru/strings.xml +++ b/assets/values-ru/strings.xml @@ -153,4 +153,12 @@ Вернитесь к приложению %s. Открыть настройки + Возврвт + Вы подтверждаете возврат покупки на сумму %s от %s? + +Отменить возврат нельзя. +Бонусный баланс будет изменен и составит %s баллов. + Возврат подтверждет + Покупка на сумму %s от %s возвращена. + Баллы diff --git a/lib/base/base_state.dart b/lib/base/base_state.dart index be78f8a..64a0e24 100644 --- a/lib/base/base_state.dart +++ b/lib/base/base_state.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:io'; import 'package:checker/resources.dart'; import 'package:flutter/material.dart'; @@ -10,7 +11,6 @@ import 'package:checker/screens/faq.dart'; import 'package:checker/strings.dart'; import 'package:checker/db.dart'; import 'package:flutter/rendering.dart'; -import 'package:meta/meta.dart'; abstract class BaseState extends State { @@ -78,7 +78,14 @@ abstract class BaseState extends State { child: getMenuItem(help_png, StringsLocalization.help()) )); - if (Theme.of(context).platform != TargetPlatform.iOS) { + if(showReturnScreen()) { + menuItemList.add(new PopupMenuItem( + value: 3, + child: getMenuItem(exit_png, StringsLocalization.returnLabel()) + )); + } + + if (Platform.isAndroid) { menuItemList.add(new PopupMenuItem( value: 2, child: getMenuItem(exit_png, StringsLocalization.exit()) @@ -95,6 +102,8 @@ abstract class BaseState extends State { ]; } + bool showReturnScreen() => false; + void onOptionsItemClick(int index) { switch (index) { case 0: { @@ -119,9 +128,14 @@ abstract class BaseState extends State { platform.invokeMethod('finish'); break; } + case 3: + openReturnScreen(); + break; } } + void openReturnScreen() {} + /// Возвращает пункт меню (Картинка с текстом) Widget getMenuItem(String image, String text) { return new Row(children: [ diff --git a/lib/network.dart b/lib/network.dart index fd85033..7f64166 100644 --- a/lib/network.dart +++ b/lib/network.dart @@ -106,3 +106,33 @@ getEndpoint() async { getToken() async { return await platform.invokeMethod('getAppToken'); } + +Future getUserPurchases(String token, int userId, int page) async { + + var headers = { + 'DM-Authorization': 'dmapptoken ${await getToken()}', + 'Authorization': 'dmtoken $token', + 'Accept-Language': StringsLocalization.localeCode + }; + + + var finalEndpoint = "${await getEndpoint()}users/$userId/purchases/?returned=false&page=$page"; + print(finalEndpoint); + + return httpClient.get(finalEndpoint, headers: headers); +} + +Future returnPurchase(String token, int userId, int purchaseId) async { + + var headers = { + 'DM-Authorization': 'dmapptoken ${await getToken()}', + 'Authorization': 'dmtoken $token', + 'Accept-Language': StringsLocalization.localeCode + }; + + + var finalEndpoint = "${await getEndpoint()}users/$userId/purchases/$purchaseId"; + print(finalEndpoint); + + return httpClient.delete(finalEndpoint, headers: headers); +} diff --git a/lib/screens/purchase.dart b/lib/screens/purchase.dart index c7309b5..5f1c51d 100644 --- a/lib/screens/purchase.dart +++ b/lib/screens/purchase.dart @@ -1,5 +1,6 @@ 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:convert'; @@ -127,6 +128,16 @@ class PurchaseScreenState extends BaseState { return listView; } + bool showReturnScreen() => true; + + void openReturnScreen() { + Future.delayed(const Duration(milliseconds: 200), () { + Route route = MaterialPageRoute(builder: (BuildContext context) => + ReturnScreen(helper, app, user['id'], user['bonus']), fullscreenDialog: true); + Navigator.push(context, route); + }); + } + getBonusInputField() { var bonusTextField = new TextField( keyboardType: TextInputType.number, diff --git a/lib/screens/return.dart b/lib/screens/return.dart new file mode 100644 index 0000000..5264612 --- /dev/null +++ b/lib/screens/return.dart @@ -0,0 +1,186 @@ +import 'dart:convert'; + +import 'package:checker/base/base_screen.dart'; +import 'package:checker/base/base_state.dart'; +import 'package:checker/db.dart'; +import 'package:checker/strings.dart'; +import 'package:flutter/material.dart'; +import 'package:checker/network.dart'; +import 'package:http/http.dart'; +import 'package:intl/intl.dart'; + +class ReturnScreen extends BaseScreen { + final int userId; + final int bonus; + + ReturnScreen(SqliteHelper helper, String app, this.userId, this.bonus) : super(helper, app); + + @override + _ReturnScreenState createState() => _ReturnScreenState(helper, app); +} + +class _ReturnScreenState extends BaseState { + static DateFormat dateFormat = DateFormat('d MMM yy'); + + ScrollController _controller; + List _purchases = []; + int _bonus; + int _page = 1; + bool _fullLoaded = false; + bool _loading = false; + + + _ReturnScreenState(SqliteHelper helper, String app) : super(helper, app); + + bool isAutomaticallyImplyLeading() => true; + + @override + Widget getScreenContent() => ListView.separated( + itemBuilder: (context, index) { + final TextStyle style = Theme.of(context).textTheme.subhead; + if(index > 0) { + final item = _purchases[index - 1]; + final DateTime date = DateTime.parse(item['date']); + final String formattedDate = dateFormat.format(date); + return GestureDetector( + child: Padding( + padding: const EdgeInsets.all(16), + child: Row( + children: [ + Expanded( + child: Text(formattedDate, style: style), + ), + Expanded( + child: Text(item['sum_total'], style: style), + ), + Expanded( + child: Text(item['sum_bonus'].toString(), style: style), + ) + ], + ), + ), + onTap: () => _confirmReturnPurchase(item, formattedDate), + ); + } else { + final TextStyle headerStyle = style.copyWith(color: Colors.grey); + return Padding( + padding: const EdgeInsets.all(16), + child: Row( + children: [ + Expanded( + child: Container(), + ), + Expanded( + child: Text(StringsLocalization.sum(), style: headerStyle), + ), + Expanded( + child: Text(StringsLocalization.points(), style: headerStyle), + ) + ], + ), + ); + } + }, + separatorBuilder: (_, index) => index == 0 + ? Container() + : Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: Divider(height: 1,), + ), + itemCount: _purchases.length > 0 ? _purchases.length + 1 : _purchases.length, + controller: _controller); + + @override + Widget build(BuildContext context) => getMainWidget(); + + @override + String getTitle() => StringsLocalization.returnLabel(); + + @override + void initState() { + super.initState(); + setState(() { + loading = true; + }); + _loadUserPurchases(); + _bonus = widget.bonus; + _controller = ScrollController(); + _controller.addListener(_scrollListener); + } + + void _scrollListener() { + if (_controller.offset >= _controller.position.maxScrollExtent && + !_controller.position.outOfRange) { + _loadUserPurchases(); + } + } + + void _loadUserPurchases() async { + if(!_fullLoaded && !_loading) { + _loading = true; + setState(() => loading = true); + String token = await helper.getToken(); + Response response = await getUserPurchases(token, widget.userId, _page); + final responseJson = json.decode(response.body); + List purchases = responseJson['results']; + _fullLoaded = _page == responseJson['pages']; + setState(() { + loading = false; + _purchases.addAll(purchases); + }); + _page++; + _loading = false; + setState(() => loading = false); + } + } + + void _confirmReturnPurchase(Map purchase, String formattedDate) { + showDialog( + context: context, + builder: (context) => + AlertDialog( + title: Text(StringsLocalization.confirmation()), + content: Text(StringsLocalization.returnConfirmation( + purchase['sum_total'], formattedDate, _bonus + purchase['sum_bonus'])), + actions: [ + FlatButton( + child: Text(StringsLocalization.no()), + onPressed: () => Navigator.pop(context), + ), + FlatButton( + child: Text(StringsLocalization.yes()), + onPressed: () { + Navigator.pop(context); + _returnPurchase(purchase, formattedDate); + } + ) + ], + ) + ); + } + + void _returnPurchase(Map purchase, String formattedDate) async { + String token = await helper.getToken(); + final Response response = await returnPurchase(token, widget.userId, purchase['id']); + if(response.statusCode == 204) { + _bonus += purchase['sum_bonus']; + setState(() { + _purchases.removeWhere((p) => p['id'] == purchase['id']); + }); + showDialog( + context: context, + builder: (context) => + AlertDialog( + title: Text(StringsLocalization.returnConfirmed()), + content: Text(StringsLocalization.returnConfirmedContent(purchase['sum_total'], formattedDate)), + actions: [ + FlatButton( + child: Text(StringsLocalization.yes()), + onPressed: () => Navigator.pop(context) + ) + ], + ) + ); + } + } +} diff --git a/lib/strings.dart b/lib/strings.dart index 8061b96..08ce633 100644 --- a/lib/strings.dart +++ b/lib/strings.dart @@ -125,6 +125,11 @@ class StringsLocalization { String trimmedVal = val.substring(0, val.length - 3); 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()]); + + static String returnConfirmedContent(String sum, String date) => + sprintf(strings['return_confirmation_content'], [sum, date]); static String registration() => strings['registration']; static String usage() => strings['usage']; @@ -218,4 +223,7 @@ class StringsLocalization { static String joysMinus() => strings['joys_minus']; static String joysHint() => strings['joys_hint']; static String phone() => strings['phone']; + static String returnLabel() => strings['returnLabel']; + static String returnConfirmed() => strings['return_confirmation']; + static String points() => strings['points']; } diff --git a/pubspec.lock b/pubspec.lock index 9f407d6..f06d8c4 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -48,6 +48,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.4.10" + intl: + dependency: "direct main" + description: + name: intl + url: "https://pub.dartlang.org" + source: hosted + version: "0.15.7" meta: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index ae07c59..2a8b048 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -9,6 +9,7 @@ dependencies: image_picker: '^0.4.1' # use for ask permissions @ iOS xml: "^3.0.0" sentry: 2.2.0 + intl: 0.15.7 flutter: sdk: flutter