1127 lines
35 KiB
Dart
1127 lines
35 KiB
Dart
import 'dart:async';
|
|
import 'dart:convert';
|
|
|
|
import 'package:dio/dio.dart';
|
|
import 'package:flutter/cupertino.dart';
|
|
import 'package:flutter/foundation.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_spinkit/flutter_spinkit.dart';
|
|
import 'package:flutter_wisetronic/models/product.dart';
|
|
import 'package:fluttertoast/fluttertoast.dart';
|
|
import 'package:hive/hive.dart';
|
|
import 'package:intl/intl.dart';
|
|
import 'package:path/path.dart' as path;
|
|
import 'package:universal_io/io.dart';
|
|
import 'package:url_launcher/url_launcher.dart';
|
|
|
|
import '../constants.dart';
|
|
import '../events/eventbus.dart';
|
|
import '../events/events.dart';
|
|
import '../generated/l10n.dart';
|
|
import '../models/business.dart';
|
|
import '../models/cart_info.dart';
|
|
import '../models/cart_line_item.dart';
|
|
import '../models/order.dart';
|
|
import '../models/user.dart';
|
|
import '../routes.dart';
|
|
import '../store/actions.dart';
|
|
import '../store/store.dart';
|
|
import 'http_util.dart';
|
|
import 'util_web.dart' if (dart.library.io) 'util_io.dart';
|
|
|
|
typedef void OnSuccess(Response response);
|
|
typedef void OnError(dynamic error);
|
|
typedef void OnComplete(dynamic data);
|
|
|
|
typedef void OnOk();
|
|
typedef void OnLoadImageError();
|
|
|
|
class Utils {
|
|
static StreamSubscription onProductWillAddToCartSubscription;
|
|
static StreamSubscription onProductWillRemoveFromCartSubscription;
|
|
|
|
init() async {
|
|
onProductWillAddToCartSubscription =
|
|
eventBus.on<OnProductWillAddToCart>().listen((event) {
|
|
CartInfo cartInfo =
|
|
getCartInfoByBusiness(store.state.cartInfos, event.business);
|
|
|
|
if (cartInfo == null || cartInfo.productList.length == 0) {
|
|
cartInfo = CartInfo();
|
|
cartInfo.id = 0;
|
|
cartInfo.amountPaid = 0.0;
|
|
cartInfo.businessInfo = event.business;
|
|
cartInfo.extraFeeList = [];
|
|
cartInfo.discountList = [];
|
|
CartLineItem lineItem = newCartLineItem(
|
|
id: 0,
|
|
price: event.price,
|
|
product: event.product,
|
|
name: event.product.name,
|
|
description: event.description,
|
|
quantity: 1.0);
|
|
cartInfo.productList = [lineItem];
|
|
addSubproductToCard(cartInfo, lineItem);
|
|
store.dispatch(new UpdateCartInfo(addCartInfoToCartInfoList(
|
|
store.state.cartInfos, cartInfo)));
|
|
eventBus.fire(new OnCartInfoUpdated());
|
|
print('#1');
|
|
} else {
|
|
if (event.product.productAttributes.length > 0) {
|
|
CartLineItem lineItem = newCartLineItem(
|
|
id: 0,
|
|
price: event.price,
|
|
product: event.product,
|
|
name: event.product.name,
|
|
description: event.description,
|
|
quantity: 1.0);
|
|
cartInfo.productList.add(lineItem);
|
|
addSubproductToCard(cartInfo, lineItem);
|
|
store.dispatch(new UpdateCartInfo(addCartInfoToCartInfoList(
|
|
store.state.cartInfos, cartInfo)));
|
|
print('#2');
|
|
eventBus.fire(new OnCartInfoUpdated());
|
|
} else {
|
|
int found = -1;
|
|
for (var i = 0; i < cartInfo.productList.length; i++) {
|
|
if (event.product.id == cartInfo.productList[i].product.id) {
|
|
found = i;
|
|
break;
|
|
}
|
|
}
|
|
if (found == -1) {
|
|
CartLineItem lineItem = newCartLineItem(
|
|
id: 0,
|
|
price: event.price,
|
|
product: event.product,
|
|
name: event.product.name,
|
|
description: event.description,
|
|
quantity: 1.0);
|
|
cartInfo.productList.add(lineItem);
|
|
addSubproductToCard(cartInfo, lineItem);
|
|
store.dispatch(new UpdateCartInfo(addCartInfoToCartInfoList(
|
|
store.state.cartInfos, cartInfo)));
|
|
print('#3');
|
|
eventBus.fire(new OnCartInfoUpdated());
|
|
} else {
|
|
if (cartInfo.productList[found].quantity + 1.0 >
|
|
event.product.leftNum) {
|
|
// Fluttertoast.showToast(
|
|
// msg: S.of(context).product_insufficient,
|
|
// toastLength: Toast.LENGTH_SHORT,
|
|
// gravity: ToastGravity.CENTER,
|
|
// backgroundColor: Colors.red,
|
|
// textColor: Colors.white);
|
|
} else {
|
|
cartInfo.productList[found].quantity += 1;
|
|
addSubproductQty(cartInfo, cartInfo.productList[found]);
|
|
store.dispatch(new UpdateCartInfo(
|
|
addCartInfoToCartInfoList(
|
|
store.state.cartInfos, cartInfo)));
|
|
print('#4');
|
|
eventBus.fire(new OnCartInfoUpdated());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
});
|
|
onProductWillRemoveFromCartSubscription =
|
|
eventBus.on<OnProductWillRemoveFromCart>().listen((event) {
|
|
CartInfo cartInfo =
|
|
getCartInfoByBusiness(store.state.cartInfos, event.business);
|
|
if (cartInfo != null) {
|
|
if (cartInfo.productList.length > 0) {
|
|
if (event.productListIndex != -1) {
|
|
if (cartInfo.productList[event.productListIndex].quantity <= 1) {
|
|
String uuid = cartInfo.productList[event.productListIndex].uuid;
|
|
cartInfo.productList.removeAt(event.productListIndex);
|
|
removeSubproduct(cartInfo, uuid);
|
|
} else {
|
|
cartInfo.productList[event.productListIndex].quantity -= 1;
|
|
addSubproductQty(cartInfo, cartInfo.productList[event.productListIndex], remove: true);
|
|
}
|
|
} else {
|
|
int productListIndex = -1;
|
|
for (var i = 0; i < cartInfo.productList.length; i++) {
|
|
if (cartInfo.productList[i].product.id == event.product.id) {
|
|
productListIndex = i;
|
|
break;
|
|
}
|
|
}
|
|
if (productListIndex != -1) {
|
|
if (cartInfo.productList[productListIndex].quantity <= 1) {
|
|
String uuid = cartInfo.productList[productListIndex].uuid;
|
|
cartInfo.productList.removeAt(productListIndex);
|
|
removeSubproduct(cartInfo, uuid);
|
|
} else {
|
|
cartInfo.productList[productListIndex].quantity -= 1;
|
|
addSubproductQty(cartInfo, cartInfo.productList[productListIndex], remove: true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (cartInfo.productList.length <= 0) {
|
|
store.dispatch(UpdateCartInfo(removeCartInfoFromCartInfoList(
|
|
store.state.cartInfos, cartInfo)));
|
|
} else {
|
|
store.dispatch(UpdateCartInfo(addCartInfoToCartInfoList(
|
|
store.state.cartInfos, cartInfo)));
|
|
}
|
|
eventBus.fire(new OnCartInfoUpdated());
|
|
}
|
|
});
|
|
}
|
|
|
|
static bool equalsIgnoreCase(String a, String b) =>
|
|
(a == null && b == null) ||
|
|
(a != null && b != null && a.toLowerCase() == b.toLowerCase());
|
|
|
|
static int selectionsContains(
|
|
Map<String, dynamic> selections, String key, String name) {
|
|
if (selections.containsKey(key.toUpperCase())) {
|
|
for (var i = 0; i < (selections[key.toUpperCase()] as List).length; i++) {
|
|
Map<String, dynamic> item = (selections[key.toUpperCase()] as List)[i];
|
|
if (Utils.equalsIgnoreCase(item['name'], name)) {
|
|
return i;
|
|
}
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static List<String> getSelectedAttributeValue(
|
|
Map<String, dynamic> selections, String key) {
|
|
List<String> valueArr = [];
|
|
if (selections.containsKey(key.toUpperCase())) {
|
|
for (var i = 0; i < (selections[key.toUpperCase()] as List).length; i++) {
|
|
valueArr.add(
|
|
(selections[key.toUpperCase()][i]['name'] as String).toLowerCase());
|
|
}
|
|
}
|
|
return valueArr;
|
|
}
|
|
|
|
static bool selectionsNotEmptyAt(
|
|
Map<String, dynamic> selections, String key) {
|
|
if (selections.containsKey(key.toUpperCase()) &&
|
|
(selections[key.toUpperCase()] as List).length > 0) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static Map<String, dynamic> stringToJson(String string) {
|
|
if (string == null || string.isEmpty) {
|
|
return null;
|
|
}
|
|
return json.decode(string);
|
|
}
|
|
|
|
static void jsonPrettyPrint(Map<String, dynamic> map) {
|
|
JsonEncoder encoder = new JsonEncoder.withIndent(' ');
|
|
String prettyPrint = encoder.convert(map);
|
|
debugPrint(prettyPrint);
|
|
}
|
|
|
|
static launchURL(String url) async {
|
|
if (await canLaunch(url)) {
|
|
await launch(url);
|
|
} else {
|
|
throw 'Could not launch $url';
|
|
}
|
|
}
|
|
|
|
static String getPlatformName() {
|
|
String platformName = '';
|
|
if (kIsWeb) {
|
|
platformName = 'web';
|
|
} else if (Platform.isAndroid) {
|
|
platformName = 'android';
|
|
} else if (Platform.isIOS) {
|
|
platformName = 'ios';
|
|
}
|
|
return platformName;
|
|
}
|
|
|
|
static Future<Box> getBox() async {
|
|
return Util.getBox();
|
|
}
|
|
|
|
static Widget imageLoadingIndicator(
|
|
{double width = 30.0, double height = 30.0}) {
|
|
return SizedBox(
|
|
width: width,
|
|
height: height,
|
|
child: Center(
|
|
child: Container(
|
|
width: 30.0,
|
|
height: 30.0,
|
|
child: CupertinoActivityIndicator(),
|
|
color: Colors.transparent,
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
static String safePhoneNumber(String phone) {
|
|
if (phone.length < 8) {
|
|
return phone;
|
|
}
|
|
String start = phone.substring(0, 3);
|
|
String end = phone.substring(phone.length - 3);
|
|
return '${start}****${end}';
|
|
}
|
|
|
|
static String safeString(String string) {
|
|
if (string == null || string.length == 0) {
|
|
return '';
|
|
}
|
|
String start = string.substring(0, 1);
|
|
String end = string.substring(string.length - 1);
|
|
return '${start}****${end}';
|
|
}
|
|
|
|
static String smartRound(double amount, int decimalPlace) {
|
|
double a = double.parse(amount.toStringAsFixed(decimalPlace));
|
|
double b = double.parse(amount.toStringAsFixed(0));
|
|
if (a - b == 0) {
|
|
return amount.toStringAsFixed(0);
|
|
}
|
|
return amount.toStringAsFixed(decimalPlace);
|
|
}
|
|
|
|
static void createOrUpdateStripePaymentMethod(
|
|
String paymentMethodId,
|
|
String cardBrand,
|
|
String paymentMethodType,
|
|
String cardCountry,
|
|
int cardExpMonth,
|
|
int cardExpYear,
|
|
String cardFunding,
|
|
String cardLast4,
|
|
) {
|
|
HttpUtil.httpPost(
|
|
'v1/create-update-stripe-payment-method',
|
|
(response) {
|
|
if (response.statusCode == 200) {
|
|
print(
|
|
'create or update customer stripe payment method success. ${response.data}');
|
|
}
|
|
},
|
|
body: {
|
|
'payment_method_id': paymentMethodId,
|
|
'card_brand': cardBrand,
|
|
'payment_method_type': paymentMethodType,
|
|
'card_country': cardCountry,
|
|
'card_exp_month': cardExpMonth,
|
|
'card_exp_year': cardExpYear,
|
|
'card_funding': cardFunding,
|
|
'card_last4': cardLast4,
|
|
},
|
|
isFormData: true,
|
|
).catchError((error) {
|
|
print('Error: ${error}');
|
|
});
|
|
}
|
|
|
|
static showSubmitDialog(BuildContext context) {
|
|
showDialog(
|
|
context: context,
|
|
barrierDismissible: false,
|
|
builder: (BuildContext context) {
|
|
return AlertDialog(
|
|
content: WillPopScope(
|
|
child: Container(
|
|
width: 300.0,
|
|
height: 150.0,
|
|
child: Center(
|
|
child: Column(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
crossAxisAlignment: CrossAxisAlignment.center,
|
|
children: <Widget>[
|
|
SpinKitThreeBounce(
|
|
color: Colors.lightBlueAccent,
|
|
size: 30.0,
|
|
),
|
|
Text(
|
|
S.of(context).submitting,
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
onWillPop: () async {
|
|
Fluttertoast.showToast(
|
|
msg: S.of(context).submitting_please_wait,
|
|
toastLength: Toast.LENGTH_SHORT,
|
|
gravity: ToastGravity.CENTER,
|
|
backgroundColor: Colors.red,
|
|
textColor: Colors.white);
|
|
return false;
|
|
}),
|
|
);
|
|
},
|
|
);
|
|
}
|
|
|
|
static showLoadingDialog(BuildContext context, {String message}) {
|
|
showDialog(
|
|
context: context,
|
|
barrierDismissible: false,
|
|
builder: (BuildContext context) {
|
|
return AlertDialog(
|
|
content: WillPopScope(
|
|
child: Container(
|
|
width: 80.0,
|
|
height: 80.0,
|
|
child: Center(
|
|
child: Column(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
crossAxisAlignment: CrossAxisAlignment.center,
|
|
children: <Widget>[
|
|
SpinKitThreeBounce(
|
|
color: Colors.lightBlueAccent,
|
|
size: 30.0,
|
|
),
|
|
Text(
|
|
message != null ? message : S.of(context).recalculating,
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
onWillPop: () async {
|
|
Fluttertoast.showToast(
|
|
msg: S.of(context).loading_please_wait,
|
|
toastLength: Toast.LENGTH_SHORT,
|
|
gravity: ToastGravity.CENTER,
|
|
backgroundColor: Colors.red,
|
|
textColor: Colors.white);
|
|
return false;
|
|
}),
|
|
);
|
|
},
|
|
);
|
|
}
|
|
|
|
static getTitleFromBody(String body) {
|
|
if (body.length <= 30) {
|
|
return body;
|
|
}
|
|
return body.substring(0, 29);
|
|
}
|
|
|
|
static void getMiniLink(
|
|
String realLink, OnSuccess onSuccess, OnError onError) {
|
|
HttpUtil.httpPost(
|
|
'get-minilink/',
|
|
(response) {
|
|
onSuccess(response);
|
|
},
|
|
body: {'link': realLink},
|
|
isFormData: true,
|
|
).catchError((error) {
|
|
onError(error);
|
|
});
|
|
}
|
|
|
|
static String getOs({bool checkWeb = false}) {
|
|
if (checkWeb && kIsWeb) {
|
|
return 'web';
|
|
}
|
|
if (Platform.isAndroid) {
|
|
return 'android';
|
|
}
|
|
if (Platform.isIOS) {
|
|
return 'ios';
|
|
}
|
|
if (Platform.isLinux) {
|
|
return 'ubuntu';
|
|
}
|
|
if (Platform.isMacOS) {
|
|
return 'mac';
|
|
}
|
|
if (Platform.isWindows) {
|
|
return 'windows';
|
|
}
|
|
return 'web';
|
|
}
|
|
|
|
static int getOsFontHex(String os) {
|
|
if (os == 'mac') {
|
|
return Constants.FONT_APPLE;
|
|
}
|
|
if (os == 'windows') {
|
|
return Constants.FONT_WINDOWS;
|
|
}
|
|
if (os == 'ubuntu') {
|
|
return Constants.FONT_UBUNTU;
|
|
}
|
|
if (os == 'android') {
|
|
return Constants.FONT_ANDROID;
|
|
}
|
|
if (os == 'ios') {
|
|
return Constants.FONT_IOS;
|
|
}
|
|
return Constants.FONT_TOPTONS;
|
|
}
|
|
|
|
static void getCurrentUser() {
|
|
getBox().then((box) {
|
|
int userId = box.get(Constants.KEY_USER_ID, defaultValue: 0);
|
|
String accessToken =
|
|
box.get(Constants.KEY_ACCESS_TOKEN, defaultValue: '');
|
|
if (userId == 0 || accessToken.isEmpty) {
|
|
eventBus.fire(new OnGetCurrentUserFailed(Error()));
|
|
} else {
|
|
HttpUtil.httpGet(
|
|
'v1/users/$userId',
|
|
queryParameters: {},
|
|
businessId: Constants.BUSINESS_ID,
|
|
).then((value) {
|
|
User user = User.fromJson(value);
|
|
store.dispatch(UpdateCurrentUser(user));
|
|
eventBus.fire(OnCurrentUserUpdated());
|
|
}).catchError((err) {
|
|
eventBus.fire(new OnGetCurrentUserFailed(err));
|
|
});
|
|
}
|
|
}).catchError((err) {
|
|
eventBus.fire(new OnGetCurrentUserFailed(err));
|
|
});
|
|
}
|
|
|
|
static void showMessageDialog(BuildContext context, dynamic error,
|
|
{String title, List<Widget> actions, OnOk onOk}) {
|
|
String message = '';
|
|
print('error $error');
|
|
if (error is DioError && error.response != null) {
|
|
if (error.response.data['message'] != null) {
|
|
message = error.response.data['message'];
|
|
} else if (error.response.data['error'] != null) {
|
|
if (error.response.data['error'] is String) {
|
|
message = error.response.data['error'];
|
|
} else if (error.response.data['error']['errmsg'] != null) {
|
|
message = error.response.data['error']['errmsg'];
|
|
} else if (error.response.data['error']['message'] != null) {
|
|
message = error.response.data['error']['message'];
|
|
} else {
|
|
message = error.response.data.toString();
|
|
}
|
|
}
|
|
} else if (error is String) {
|
|
message = '$error';
|
|
} else if (error.message != null) {
|
|
message = error.message;
|
|
} else {
|
|
message = '$error';
|
|
}
|
|
showDialog(
|
|
context: context,
|
|
builder: (BuildContext context) {
|
|
return AlertDialog(
|
|
title: Text(title != null ? title : S.of(context).error),
|
|
content: Text(message),
|
|
actions: actions == null
|
|
? <Widget>[
|
|
FlatButton(
|
|
child: Text(S.of(context).ok),
|
|
onPressed: onOk == null
|
|
? () {
|
|
Navigator.of(context).pop();
|
|
}
|
|
: onOk,
|
|
),
|
|
]
|
|
: actions,
|
|
);
|
|
});
|
|
}
|
|
|
|
static Future<List<int>> getLastVisit() async {
|
|
Box box = await getBox();
|
|
List<dynamic> lastVisit =
|
|
json.decode(box.get(Constants.KEY_LAST_VISIT, defaultValue: '[]'));
|
|
List<int> lv = lastVisit.map((e) => e as int).toList();
|
|
store.dispatch(UpdateLastVisit(lv));
|
|
return lv;
|
|
}
|
|
|
|
static void saveLastVisit(List<int> lastVisit) {
|
|
getBox().then((box) {
|
|
box.put(Constants.KEY_LAST_VISIT, lastVisit.toString());
|
|
}).catchError((error) {
|
|
print('Error occurred: $error');
|
|
});
|
|
}
|
|
|
|
static String utcDatetimeStringToLocalDatetimeString(
|
|
BuildContext context, String utcDatetimeString,
|
|
{bool withTime = false}) {
|
|
var utcDatetime;
|
|
try {
|
|
utcDatetime = DateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
|
|
.parse(utcDatetimeString, true);
|
|
} catch (error) {
|
|
utcDatetime = DateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'")
|
|
.parse(utcDatetimeString, true);
|
|
}
|
|
var localDatetime = utcDatetime.toLocal();
|
|
return datetimeFormat(context, localDatetime, withTime: withTime);
|
|
}
|
|
|
|
static String datetimeFormat(BuildContext context, DateTime date,
|
|
{bool withTime = false}) {
|
|
final theDate = DateTime(date.year, date.month, date.day);
|
|
final now = DateTime.now();
|
|
final today = DateTime(now.year, now.month, now.day);
|
|
final yesterday = DateTime(now.year, now.month, now.day - 1);
|
|
final tomorrow = DateTime(now.year, now.month, now.day + 1);
|
|
DateFormat formatter = DateFormat('yyyy/MM/dd');
|
|
DateFormat timeFormatter = DateFormat('HH:mm');
|
|
if (withTime != null && withTime) {
|
|
formatter = DateFormat('yyyy/MM/dd HH:mm:ss');
|
|
}
|
|
if (theDate == today) {
|
|
return S.of(context).today_with_time(timeFormatter.format(date));
|
|
}
|
|
if (theDate == tomorrow) {
|
|
return S.of(context).tomorrow_with_time(timeFormatter.format(date));
|
|
}
|
|
return formatter.format(date);
|
|
}
|
|
|
|
void uploadPicture(
|
|
BuildContext context,
|
|
File image,
|
|
Map<String, dynamic> product,
|
|
int imageId,
|
|
String currentImageUrl,
|
|
OnSuccess onSuccess,
|
|
{int originalImageId = 0}) async {
|
|
if (image != null && await image.exists()) {
|
|
print('image: ${image.path}');
|
|
String imageName = path.basename(image.path);
|
|
MultipartFile imageFile =
|
|
await MultipartFile.fromFile(image.path, filename: imageName);
|
|
Map<String, dynamic> formMap = {
|
|
'id': product['id'],
|
|
'image': imageFile,
|
|
'original_image_id': originalImageId
|
|
};
|
|
if (imageId == -1) {
|
|
HttpUtil.httpPost(
|
|
'upload-product-image/',
|
|
(response) {
|
|
if (onSuccess != null) {
|
|
onSuccess(response);
|
|
}
|
|
},
|
|
isFormData: true,
|
|
body: formMap,
|
|
sendProgress: (int sent, int total) {
|
|
if (sent == total) {
|
|
eventBus.fire(OnUploadImageProgressEvent(imageId, false, 1.0));
|
|
} else {
|
|
eventBus.fire(
|
|
OnUploadImageProgressEvent(imageId, true, sent / total));
|
|
}
|
|
},
|
|
).catchError((error) {
|
|
Utils.showMessageDialog(context, error);
|
|
});
|
|
} else {
|
|
HttpUtil.httpPost(
|
|
'upload-product-gallery-image/',
|
|
(response) {
|
|
if (onSuccess != null) {
|
|
onSuccess(response);
|
|
}
|
|
},
|
|
isFormData: true,
|
|
body: formMap,
|
|
sendProgress: (int sent, int total) {
|
|
if (sent == total) {
|
|
eventBus.fire(OnUploadImageProgressEvent(imageId, false, 0.0));
|
|
} else {
|
|
eventBus.fire(
|
|
OnUploadImageProgressEvent(imageId, true, sent / total));
|
|
}
|
|
},
|
|
).catchError((error) {
|
|
Utils.showMessageDialog(context, error);
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
static CartInfo getCartInfoByBusiness(
|
|
List<CartInfo> cartInfos, Business business) {
|
|
if (cartInfos == null || cartInfos.length <= 0) {
|
|
return null;
|
|
}
|
|
for (var i = 0; i < cartInfos.length; i++) {
|
|
if (cartInfos[i].businessInfo.id == business.id) {
|
|
return cartInfos[i];
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
static CartInfo getCartInfoByBusinessId(
|
|
List<CartInfo> cartInfos, int businessId) {
|
|
if (cartInfos == null || cartInfos.length <= 0) {
|
|
return null;
|
|
}
|
|
for (var i = 0; i < cartInfos.length; i++) {
|
|
if (cartInfos[i].businessInfo.id == businessId) {
|
|
return cartInfos[i];
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
static List<CartInfo> addCartInfoToCartInfoList(
|
|
List<CartInfo> cartInfos, CartInfo cartInfo) {
|
|
if (cartInfos == null || cartInfos.length <= 0) {
|
|
cartInfos = [cartInfo];
|
|
} else {
|
|
bool found = false;
|
|
for (var i = 0; i < cartInfos.length; i++) {
|
|
if (cartInfos[i].businessInfo.id == cartInfo.businessInfo.id) {
|
|
cartInfos[i] = cartInfo;
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!found) {
|
|
cartInfos.add(cartInfo);
|
|
}
|
|
}
|
|
Utils.getBox().then((box) {
|
|
box.put(Constants.KEY_CARTINFOS, cartInfos.toString());
|
|
}).catchError((error) {
|
|
print('Error occurred: $error');
|
|
});
|
|
return cartInfos;
|
|
}
|
|
|
|
static List<CartInfo> removeCartInfoFromCartInfoList(
|
|
List<CartInfo> cartInfos, CartInfo cartInfo) {
|
|
if (cartInfos == null || cartInfos.length <= 0 || cartInfo == null) {
|
|
return [];
|
|
}
|
|
int removeIndex = -1;
|
|
for (var i = 0; i < cartInfos.length; i++) {
|
|
if (cartInfo.businessInfo.id == cartInfos[i].businessInfo.id) {
|
|
removeIndex = i;
|
|
break;
|
|
}
|
|
}
|
|
if (removeIndex != -1) {
|
|
cartInfos.removeAt(removeIndex);
|
|
}
|
|
getBox().then((box) {
|
|
box.put(Constants.KEY_CARTINFOS, cartInfos.toString());
|
|
}).catchError((error) {
|
|
print('Error occurred: $error');
|
|
});
|
|
return cartInfos;
|
|
}
|
|
|
|
static void clearCart(BuildContext context, CartInfo cartInfo,
|
|
{OnComplete onComplete}) {
|
|
if (cartInfo != null && cartInfo.productList.length > 0) {
|
|
showDialog(
|
|
context: context,
|
|
builder: (BuildContext context) {
|
|
return AlertDialog(
|
|
title: Text(S.of(context).warning),
|
|
content: Text(S.of(context).are_you_sure_to_empty_basket),
|
|
actions: <Widget>[
|
|
FlatButton(
|
|
child: Text(S.of(context).cancel),
|
|
onPressed: () {
|
|
Navigator.of(context).pop();
|
|
},
|
|
),
|
|
FlatButton(
|
|
child: Text(S.of(context).yes_i_am_sure),
|
|
onPressed: () {
|
|
store.dispatch(new UpdateCartInfo(
|
|
Utils.removeCartInfoFromCartInfoList(
|
|
store.state.cartInfos, cartInfo)));
|
|
eventBus.fire(new OnCartInfoUpdated());
|
|
Navigator.of(context).pop();
|
|
if (onComplete != null) {
|
|
onComplete(null);
|
|
}
|
|
},
|
|
)
|
|
],
|
|
);
|
|
});
|
|
}
|
|
}
|
|
|
|
static void requireLogin(BuildContext context,
|
|
{String returnUrl, bool replace = true, int delayMilliseconds = 400}) {
|
|
Future.delayed(Duration(milliseconds: delayMilliseconds), () {
|
|
if (returnUrl != null) {
|
|
store.dispatch(UpdateRedirectRoute(returnUrl));
|
|
}
|
|
Routes.router.navigateTo(context, '/login', replace: replace);
|
|
});
|
|
}
|
|
|
|
static String getFirstNumberFromString(String numString) {
|
|
final intInStr = RegExp(r'\d+');
|
|
return intInStr.firstMatch(numString).group(0);
|
|
}
|
|
|
|
static void loadProducts(int businessId, int categoryId, int page, bool more,
|
|
OnComplete onComplete, OnError onError, {int featuredCount=4, int hotSaleCount=4, String keyword=''}) {
|
|
print('feature $featuredCount');
|
|
HttpUtil.httpGet(
|
|
more ? 'v1/service-category-products-more' : 'v1/service-category-products',
|
|
businessId: businessId,
|
|
queryParameters: {
|
|
// 'lat': store.state.position.latitude.toString(),
|
|
// 'lng': store.state.position.longitude.toString(),
|
|
'category_id': categoryId,
|
|
'preview': Constants.isPreview,
|
|
'page': page,
|
|
'num_per_page': Constants.ORDERS_PER_PAGE,
|
|
'featured_count': featuredCount,
|
|
'hot_sale_count': hotSaleCount,
|
|
'keyword': Uri.encodeComponent(keyword),
|
|
},
|
|
).then((value) {
|
|
if (onComplete != null) {
|
|
onComplete(value);
|
|
}
|
|
}).catchError((error) {
|
|
if (onError != null) {
|
|
onError(error);
|
|
}
|
|
});
|
|
}
|
|
|
|
static String timestampToString(BuildContext context, int timestamp, {bool withTime=false}) {
|
|
DateTime date = DateTime.fromMillisecondsSinceEpoch(timestamp * 1000);
|
|
if (timestamp == 0) {
|
|
return '';
|
|
}
|
|
return datetimeFormat(context, date, withTime: withTime);
|
|
}
|
|
|
|
static String errorMsg2(BuildContext context, String code, String defaultString) {
|
|
switch (code) {
|
|
case 'no_delivery':
|
|
return S.of(context).checkout_no_deliver;
|
|
case 'no_shipping_address':
|
|
return S.of(context).please_provide_shipping_address;
|
|
case 'over_delivery_distance':
|
|
return S.of(context).over_delivery_distance;
|
|
case 'amount_not_meet':
|
|
return S.of(context).amount_not_meet;
|
|
case 'select_canada_post_shipping_rate':
|
|
return S.of(context).select_canada_post_shipping_rate;
|
|
case 'store_close':
|
|
return S.of(context).store_closed;
|
|
}
|
|
return defaultString;
|
|
}
|
|
|
|
static String knownName(BuildContext context, String name) {
|
|
switch (name) {
|
|
case 'pickup-discount':
|
|
return S.of(context).pickup_discount;
|
|
case 'collect-tips':
|
|
return S.of(context).service_fee;
|
|
default:
|
|
return name;
|
|
}
|
|
}
|
|
|
|
static String getWeekDayName(BuildContext context, int timestamp, bool shortName) {
|
|
DateTime date = DateTime.fromMillisecondsSinceEpoch(timestamp * 1000);
|
|
switch(date.weekday) {
|
|
case 1:
|
|
if (shortName) {
|
|
return S.of(context).mon;
|
|
} else {
|
|
return S.of(context).monday;
|
|
}
|
|
break;
|
|
case 2:
|
|
if (shortName) {
|
|
return S.of(context).tue;
|
|
} else {
|
|
return S.of(context).tuesday;
|
|
}
|
|
break;
|
|
case 3:
|
|
if (shortName) {
|
|
return S.of(context).wed;
|
|
} else {
|
|
return S.of(context).wednesday;
|
|
}
|
|
break;
|
|
case 4:
|
|
if (shortName) {
|
|
return S.of(context).thu;
|
|
} else {
|
|
return S.of(context).thursday;
|
|
}
|
|
break;
|
|
case 5:
|
|
if (shortName) {
|
|
return S.of(context).fri;
|
|
} else {
|
|
return S.of(context).friday;
|
|
}
|
|
break;
|
|
case 6:
|
|
if (shortName) {
|
|
return S.of(context).sat;
|
|
} else {
|
|
return S.of(context).saturday;
|
|
}
|
|
break;
|
|
case 7:
|
|
if (shortName) {
|
|
return S.of(context).sun;
|
|
} else {
|
|
return S.of(context).sunday;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void stripePaymentIntent(Order order, String customerId, String paymentMethodId, String cardType,
|
|
OnSuccess onSuccess, OnError onError,
|
|
{
|
|
String cardBrand,
|
|
String cardCountry,
|
|
int cardExpMonth,
|
|
int cardExpYear,
|
|
String cardFunding,
|
|
String cardLast4,
|
|
}) {
|
|
|
|
HttpUtil.httpPost('v1/stripe-payment-intent',
|
|
onSuccess,
|
|
body: {
|
|
'order_id': order.id,
|
|
'pay_amount': order.totalPrice,
|
|
'customer_id': customerId == null ? '' : customerId,
|
|
'payment_method_id': paymentMethodId,
|
|
'payment_method_type': cardType,
|
|
'card_brand': cardBrand,
|
|
'card_country': cardCountry,
|
|
'card_exp_month': cardExpMonth,
|
|
'card_exp_year': cardExpYear,
|
|
'card_funding': cardFunding,
|
|
'card_last4': cardLast4,
|
|
},
|
|
isFormData: true,
|
|
).catchError(onError);
|
|
}
|
|
|
|
static void stripeChargedSuccess(Order order, String paymentMethodId,
|
|
String paymentIntentId, OnSuccess onSuccess, OnError onError) {
|
|
HttpUtil.httpPost('v1/stripe-charged-success',
|
|
onSuccess,
|
|
isFormData: true,
|
|
body: {
|
|
'order_id': order.id,
|
|
'payment_method_id': paymentMethodId,
|
|
'payment_intent_id': paymentIntentId,
|
|
}
|
|
).catchError(onError);
|
|
}
|
|
|
|
static int getProductLineInOrder(CartInfo cartInfo) {
|
|
int qty = 0;
|
|
for (CartLineItem lineItem in cartInfo.productList) {
|
|
if (lineItem.product.id != null) {
|
|
qty += 1;
|
|
}
|
|
}
|
|
return qty;
|
|
}
|
|
|
|
static void orderAgain(BuildContext context, CartInfo cartInfo) {
|
|
cartInfo.productList.removeWhere((element) => element.product == null || element.product.id == null);
|
|
store.dispatch(UpdateCartInfo(Utils.addCartInfoToCartInfoList(store.state.cartInfos, cartInfo)));
|
|
eventBus.fire(new OnCartInfoUpdated());
|
|
Routes.router.navigateTo(context, '/checkout/${cartInfo.businessInfo.id}');
|
|
}
|
|
|
|
static String getOrderStatus(BuildContext context, int statusCode) {
|
|
switch(statusCode) {
|
|
case Constants.STATUS_PENDING:
|
|
return S.of(context).pending;
|
|
case Constants.STATUS_ACCEPT:
|
|
return S.of(context).order_acceipt;
|
|
case Constants.STATUS_PROCESSING:
|
|
return S.of(context).order_processing;
|
|
case Constants.STATUS_COMPLETE:
|
|
return S.of(context).order_complete;
|
|
case Constants.STATUS_CANCELLED:
|
|
return S.of(context).order_cancelled;
|
|
default:
|
|
return S.of(context).pending;
|
|
}
|
|
}
|
|
|
|
static CartLineItem newCartLineItem(
|
|
{int id,
|
|
double price,
|
|
Product product,
|
|
String name,
|
|
String description,
|
|
double quantity,
|
|
String parentUuid,
|
|
}) {
|
|
CartLineItem lineItem = CartLineItem();
|
|
lineItem.unitPrice = price;
|
|
lineItem.product = product;
|
|
lineItem.name = product.name;
|
|
lineItem.description = description;
|
|
lineItem.quantity = quantity;
|
|
lineItem.parentUuid = parentUuid;
|
|
return lineItem;
|
|
}
|
|
|
|
static void addSubproductToCard(CartInfo cartInfo, CartLineItem lineItem) {
|
|
if (lineItem.product.subproducts.length > 0) {
|
|
for (int j = 0; j < lineItem.product.subproducts.length; j++) {
|
|
cartInfo.productList.add(
|
|
newCartLineItem(
|
|
id: 0,
|
|
price: 0.0,
|
|
product: Product(
|
|
lineItem.product.subproducts[j].product.id,
|
|
lineItem.product.businessId,
|
|
lineItem.product.subproducts[j].product.name,
|
|
lineItem.product.subproducts[j].product.price,
|
|
lineItem.product.subproducts[j].product.description,
|
|
lineItem.product.subproducts[j].product.image,
|
|
lineItem.product.subproducts[j].product.productAttributes
|
|
),
|
|
name: lineItem.product.subproducts[j].product.name,
|
|
description: lineItem.product.subproducts[j].product.description,
|
|
quantity: 1.0,
|
|
parentUuid: lineItem.uuid
|
|
)
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void addSubproductQty(CartInfo cartInfo, CartLineItem lineItem,
|
|
{bool remove = false}) {
|
|
if (lineItem.product.subproducts.length > 0) {
|
|
for (var i = 0; i < cartInfo.productList.length; i++) {
|
|
CartLineItem lineItem1 = cartInfo.productList[i];
|
|
if (lineItem1.parentUuid == lineItem.uuid) {
|
|
for (var j = 0; j < lineItem.product.subproducts.length; j++) {
|
|
if (lineItem.product.subproducts[j].product.id == lineItem1.product.id) {
|
|
if (remove) {
|
|
lineItem1.quantity -= lineItem.product.subproducts[j].quantity;
|
|
} else {
|
|
lineItem1.quantity += lineItem.product.subproducts[j].quantity;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void removeSubproduct(CartInfo cartInfo, String uuid) {
|
|
cartInfo.productList.removeWhere((element) => element.parentUuid == uuid);
|
|
}
|
|
|
|
static void openEmail(String email) async {
|
|
final Uri params = Uri(
|
|
scheme: 'mailto',
|
|
path: '$email',
|
|
);
|
|
String url = params.toString();
|
|
if (await canLaunch(url)) {
|
|
await launchURL(url);
|
|
} else {
|
|
print('Could not launch $url');
|
|
}
|
|
}
|
|
|
|
static void callPhone(String phone) async {
|
|
String callNo = 'tel://$phone';
|
|
if (await canLaunch(callNo)) {
|
|
await launchURL(callNo);
|
|
} else {
|
|
print('Could not call $phone');
|
|
}
|
|
}
|
|
|
|
static Widget buildLine(String name, dynamic value, {double nameSize, double valueSize}) {
|
|
Row row = Row(
|
|
mainAxisAlignment: MainAxisAlignment.start,
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [],
|
|
);
|
|
row.children.add(
|
|
Container(
|
|
width: 150,
|
|
padding: EdgeInsets.only(),
|
|
child: Text(
|
|
name,
|
|
style: TextStyle(
|
|
color: Colors.black38,
|
|
fontSize: nameSize,
|
|
),
|
|
),
|
|
),
|
|
);
|
|
if (value is String) {
|
|
row.children.add(
|
|
Expanded(
|
|
child: Align(
|
|
alignment: Alignment.centerRight,
|
|
child: Text(
|
|
value,
|
|
style: TextStyle(
|
|
fontSize: valueSize,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
} else {
|
|
row.children.add(
|
|
Expanded(child: value),
|
|
);
|
|
}
|
|
return Container(
|
|
padding: EdgeInsets.only(top: 8, bottom: 8),
|
|
child: row,
|
|
);
|
|
}
|
|
|
|
static int getBusinessId() {
|
|
return Constants.BUSINESS_ID;
|
|
}
|
|
}
|
|
|
|
class RuntimeError extends Error {
|
|
final int code;
|
|
final String message;
|
|
RuntimeError(this.message, {this.code});
|
|
String toString() => "Runtime Error: $message";
|
|
}
|