524 lines
15 KiB
Dart
524 lines
15 KiB
Dart
import 'dart:async';
|
|
import 'dart:io';
|
|
|
|
import 'package:cloud_firestore/cloud_firestore.dart';
|
|
import 'package:device_info/device_info.dart';
|
|
import 'package:dio/dio.dart';
|
|
import 'package:fcs/fcs/common/services/services.dart';
|
|
import 'package:fcs/vo/payment_method.dart';
|
|
import 'package:firebase_auth/firebase_auth.dart';
|
|
import 'package:firebase_messaging/firebase_messaging.dart';
|
|
import 'package:flutter/foundation.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:logging/logging.dart';
|
|
import 'package:package_info/package_info.dart';
|
|
import 'package:path/path.dart' as Path;
|
|
import 'package:fcs/model/shared_pref.dart';
|
|
import 'package:fcs/util.dart';
|
|
import 'package:fcs/vo/bank_account.dart';
|
|
import 'package:fcs/vo/buyer.dart';
|
|
import 'package:fcs/vo/setting.dart';
|
|
import 'package:fcs/widget/NetworkConnectivity.dart';
|
|
|
|
import '../config.dart';
|
|
import '../vo/status.dart';
|
|
import '../vo/user.dart';
|
|
import '../fcs/common/helpers/api_helper.dart';
|
|
import 'base_model.dart';
|
|
import 'constants.dart';
|
|
import 'firebase_helper.dart';
|
|
|
|
class ImplementInterfaceModel {
|
|
void initUser(User user) => {};
|
|
void initSetting(Setting setting) => {};
|
|
}
|
|
|
|
class MainModel extends ChangeNotifier {
|
|
final log = Logger('MainModel');
|
|
|
|
final FirebaseAuth auth = FirebaseAuth.instance;
|
|
FirebaseMessaging firebaseMessaging;
|
|
|
|
List<BaseModel> models = [];
|
|
User user;
|
|
Buyer buyer;
|
|
FirebaseUser firebaseUser;
|
|
StreamSubscription<DocumentSnapshot> userListener;
|
|
StreamSubscription<DocumentSnapshot> buyerListener;
|
|
bool pinRequired;
|
|
Timer pinTimer;
|
|
|
|
User customer = User(
|
|
name: "Ko Myo Min",
|
|
phoneNumber: '+95 9 444444444',
|
|
fcsID: 'FCS-0203-390-2',
|
|
shippingAddress:
|
|
'154-19 64th Ave.Flushing, \nNY 11367 \nTEL. +1 (929) 215-2247',
|
|
deliveryAddress: '39 42th St. Kyaut Ta Thar Township Yangon');
|
|
|
|
User recipient = User(
|
|
name: "Ko Myo Min",
|
|
phoneNumber: '+95 9 444444444',
|
|
shippingAddress: '154-19 64th Ave.Flushing, \nNY 11367',
|
|
deliveryAddress: '39 42th St. Kyaut Ta Thar Township Yangon');
|
|
|
|
Setting setting = Setting(
|
|
terms:
|
|
'[{"insert":"* Minimum shipping weight is 1lbs.\n* Oversized goods, Light weight/Large volume items, laptops, phones, tablets may incur extra charges based on pecifications.Please contact us for pricing.\n* Goods with lithium battary needs extra packaging and declaration. Please inform us ahead of time so that we can process your package accordingly.\n* Loose Batteries, Drones, and Prescription medicines are not allowed on aircraft.\n* Payment: We accept money orders, any US bank transfers via Zelle, AYA, KBZ and CB. No COD except for pick-ups.\n*Payments made in Myanmar will incur 2% tranfer fee\n"}]');
|
|
PackageInfo packageInfo;
|
|
bool isLoaded = true;
|
|
bool isOnline = true;
|
|
|
|
static const PIN_TIME_MIN = 10;
|
|
|
|
MainModel() {
|
|
// NetworkConnectivity.instance.statusStream.listen((data) {
|
|
// bool _isOnline = data["isOnline"];
|
|
// if (_isOnline && !this.isOnline) {
|
|
// init();
|
|
// }
|
|
// this.isOnline = _isOnline;
|
|
// notifyListeners();
|
|
// });
|
|
_loadFcs();
|
|
Services.instance.authService.onAuthStatus().listen((event) {
|
|
print("main event-->$event");
|
|
});
|
|
}
|
|
|
|
List<PaymentMethod> get paymentMethods {
|
|
List<PaymentMethod> methods = [
|
|
PaymentMethod(
|
|
name: 'AYA Bank',
|
|
accountName: 'FCS',
|
|
account: '100 23404320548398',
|
|
phone: '+959123456789',
|
|
mail: 'aya@gmail.com'),
|
|
PaymentMethod(
|
|
name: 'KBZ Bank',
|
|
accountName: 'FCS',
|
|
account: '100 23404320548398',
|
|
phone: '+959123456789',
|
|
mail: 'kbz@gmail.com'),
|
|
PaymentMethod(
|
|
name: 'PayPal',
|
|
accountName: 'FCS',
|
|
link: 'https://www.paypal.com/donate/buttons',
|
|
),
|
|
];
|
|
return methods;
|
|
}
|
|
|
|
_loadFcs() async {
|
|
user = await SharedPref.getUser();
|
|
notifyListeners();
|
|
}
|
|
|
|
saveUser(String pin, String phone) {
|
|
if (pin == "000000") {
|
|
user = User(name: "Owner", phoneNumber: phone);
|
|
SharedPref.saveUser(user);
|
|
} else {
|
|
user = User(name: "Customer", phoneNumber: phone);
|
|
SharedPref.saveUser(user);
|
|
}
|
|
notifyListeners();
|
|
}
|
|
|
|
resetPinTimer() {
|
|
if (pinTimer != null && pinTimer.isActive) {
|
|
pinTimer.cancel();
|
|
}
|
|
pinRequired = false;
|
|
pinTimer = Timer(Duration(minutes: PIN_TIME_MIN), () {
|
|
pinRequired = true;
|
|
});
|
|
}
|
|
|
|
bool isLogin() {
|
|
return this.user != null;
|
|
}
|
|
|
|
bool isCustomer() {
|
|
return user != null && user.name != "Owner";
|
|
}
|
|
|
|
bool isOwner() {
|
|
return user != null && user.name == "Owner";
|
|
}
|
|
|
|
bool hasEmail() {
|
|
return this.user != null && this.user.isEmail();
|
|
}
|
|
|
|
bool agreedTerm() {
|
|
return this.user != null && this.user.agreeTerms;
|
|
}
|
|
|
|
bool isBuyer() {
|
|
return this.user == null || this.user.isBuyer();
|
|
}
|
|
|
|
bool isRegBuyer() {
|
|
return isBuyer() && buyer != null;
|
|
}
|
|
|
|
bool isApprovedBuyer() {
|
|
return isBuyer() && buyer != null && buyer.isApproved();
|
|
}
|
|
|
|
bool isSysAdmin() {
|
|
return this.user != null && this.user.isSysAdmin();
|
|
}
|
|
|
|
bool isSysSupport() {
|
|
return this.user != null && this.user.isSysSupport();
|
|
}
|
|
|
|
bool isBizAdmin() {
|
|
return this.user != null && this.user.isBizAdmin();
|
|
}
|
|
|
|
bool isOwnerAndAbove() {
|
|
return this.user != null && this.user.isOwnerAndAbove();
|
|
}
|
|
|
|
bool isAdmin() {
|
|
return this.user != null && this.user.hasAdmin();
|
|
}
|
|
|
|
bool showHistoryBtn() {
|
|
return isSysAdmin() || isSysSupport() || isBizAdmin();
|
|
}
|
|
|
|
init() async {
|
|
// await _loadSetting();
|
|
// _loadUser();
|
|
// resetPinTimer();
|
|
}
|
|
|
|
void addModel(BaseModel model) {
|
|
models.add(model);
|
|
}
|
|
|
|
void _initUser(User user) {
|
|
models.forEach((m) => m.initUser(user));
|
|
|
|
if (firebaseMessaging != null) {
|
|
firebaseMessaging.subscribeToTopic(user.docID);
|
|
}
|
|
}
|
|
|
|
void _initSetting(Setting setting) {
|
|
models.forEach((m) => m.initSetting(setting));
|
|
}
|
|
|
|
Future<void> _loadSetting() async {
|
|
this.setting = await _getSetting();
|
|
this.packageInfo = await PackageInfo.fromPlatform();
|
|
_initSetting(setting);
|
|
}
|
|
|
|
void _loadUser() async {
|
|
this.firebaseUser = await auth.currentUser();
|
|
if (this.firebaseUser == null) {
|
|
this.isLoaded = true;
|
|
notifyListeners();
|
|
return;
|
|
}
|
|
|
|
_logUser(this.firebaseUser);
|
|
|
|
// load from local, if successful,notify listeners
|
|
User _user = await SharedPref.getUser();
|
|
if (_user != null) {
|
|
await _user.setFirebaseUser(this.firebaseUser);
|
|
_initUser(_user);
|
|
this.user = _user;
|
|
|
|
if (this.user.isRegisteredBuyer()) {
|
|
_loadBuyer();
|
|
}
|
|
this.isLoaded = true;
|
|
notifyListeners();
|
|
log.info("user loaded from shared pref!");
|
|
}
|
|
|
|
_listenUser();
|
|
}
|
|
|
|
void _listenUser() {
|
|
if (this.userListener != null) {
|
|
this.userListener.cancel();
|
|
}
|
|
|
|
this.userListener = getDocSnapshot(
|
|
"/$biz_collection/${setting.okEnergyId}/$user_collection",
|
|
firebaseUser.uid)
|
|
.listen((userSnap) async {
|
|
if (userSnap.exists) {
|
|
User _user = User.fromMap(userSnap.data, userSnap.documentID);
|
|
|
|
// load claims
|
|
try {
|
|
FirebaseUser _firebaseUser = await getProfile(this.firebaseUser);
|
|
|
|
await _user.setFirebaseUser(_firebaseUser);
|
|
_initUser(_user);
|
|
this.user = _user;
|
|
this.firebaseUser = _firebaseUser;
|
|
await SharedPref.saveUser(this.user);
|
|
} catch (e) {
|
|
log.warning(e.toString());
|
|
}
|
|
|
|
log.info(
|
|
"_loadUser => ID: ${this.user.docID}, AccountID: ${this.user.accountID},"
|
|
"BizID: ${this.user.accountID},"
|
|
", Privileges: ${this.user.claimPrivileges}, isSysAdmin: ${this.user.isSysAdmin()}");
|
|
|
|
if (this.user.isRegisteredBuyer()) {
|
|
_loadBuyer();
|
|
}
|
|
this.isLoaded = true;
|
|
notifyListeners();
|
|
}
|
|
});
|
|
}
|
|
|
|
void _loadBuyer() async {
|
|
if (this.user == null) return;
|
|
if (buyerListener != null) buyerListener.cancel();
|
|
buyerListener = getDocSnapshot(
|
|
"/$biz_collection/${setting.okEnergyId}/$buyer_collection",
|
|
this.user.docID)
|
|
.listen((buyerSnap) async {
|
|
if (buyerSnap.exists) {
|
|
this.buyer = Buyer.fromMap(buyerSnap.data, buyerSnap.documentID);
|
|
} else {
|
|
this.buyer = null;
|
|
}
|
|
notifyListeners();
|
|
});
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
super.dispose();
|
|
// if (this.userListener != null) {
|
|
// this.userListener.cancel();
|
|
// }
|
|
// SharedPref.removeUser();
|
|
// this.user = User();
|
|
}
|
|
|
|
Future<void> login(String phoneNumber, String pass) async {
|
|
var id = phoneNumber.replaceFirst("+", "");
|
|
id = updatePhoneNumber(id);
|
|
|
|
var data = {"id": id, "password": pass};
|
|
var result = await requestAPI("/login", "POST", payload: data);
|
|
|
|
var token = result["Token"];
|
|
|
|
// login with custom token
|
|
// AuthResult r = await this.auth.signInWithCustomToken(token: token);
|
|
// this.firebaseUser = r.user;
|
|
isLoaded = false;
|
|
_loadUser();
|
|
_logUser(this.firebaseUser);
|
|
}
|
|
|
|
Future<FirebaseUser> getProfile(FirebaseUser firebaseUser) async {
|
|
// IdTokenResult idtoken = await firebaseUser.getIdToken();
|
|
var data = await requestAPI(
|
|
"/profile",
|
|
"GET",
|
|
token: "", //idtoken.token,
|
|
);
|
|
var _token = data["Token"];
|
|
// AuthResult a = await this.auth.signInWithCustomToken(token: _token);
|
|
// return a.user;
|
|
return null;
|
|
}
|
|
|
|
Future<void> _logUser(FirebaseUser firebaseUser) async {
|
|
// IdTokenResult idtoken = await firebaseUser.getIdToken();
|
|
|
|
await requestAPI(
|
|
"/log",
|
|
"GET",
|
|
token: "", //idtoken.token,
|
|
);
|
|
}
|
|
|
|
Future<void> logout() async {
|
|
this.user = null;
|
|
notifyListeners();
|
|
return;
|
|
|
|
if (this.userListener != null) {
|
|
await this.userListener.cancel();
|
|
}
|
|
|
|
await auth.signOut();
|
|
this.user = null;
|
|
this.buyer = null;
|
|
this.firebaseUser = null;
|
|
await SharedPref.removeUser();
|
|
if (firebaseMessaging != null) {
|
|
firebaseMessaging.unsubscribeFromTopic(user.docID);
|
|
}
|
|
|
|
// logout models
|
|
models.forEach((m) => m.logout());
|
|
|
|
notifyListeners();
|
|
}
|
|
|
|
Future<void> signup(
|
|
String name, password, confirmPassword, phoneNumber) async {
|
|
if (password == "" || password.length < 6) {
|
|
throw Exception("Password must be at least 6 characters");
|
|
}
|
|
if (password != confirmPassword) {
|
|
throw Exception("Password mismatch");
|
|
}
|
|
var id = phoneNumber.replaceFirst("+", "");
|
|
id = updatePhoneNumber(id);
|
|
|
|
var inputData = {"id": id, "password": password, "user_name": name};
|
|
DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
|
|
AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo;
|
|
String deviceName = "${androidInfo.model}(${androidInfo.id})";
|
|
|
|
var url = "${Config.instance.apiURL}/signup";
|
|
Response response = await Dio().post(url,
|
|
data: inputData,
|
|
options: Options(
|
|
headers: {"Device": androidInfo.androidId + ":" + deviceName}));
|
|
|
|
var data = Status.fromJson(response.data);
|
|
if (data.status != 'Ok') {
|
|
throw Exception("${data.errorCode} : ${data.message}");
|
|
}
|
|
}
|
|
|
|
Future<void> confirmSignup(
|
|
String phoneNumber, password, confirmSMSCode) async {
|
|
var id = phoneNumber.replaceFirst("+", "");
|
|
id = updatePhoneNumber(id);
|
|
|
|
if (confirmSMSCode == "" || confirmSMSCode.length != 6) {
|
|
throw Exception("Password must be 6 characters");
|
|
}
|
|
|
|
var inputData = {
|
|
"id": id,
|
|
"password": password,
|
|
"confirmation_code": confirmSMSCode
|
|
};
|
|
var url = "${Config.instance.apiURL}/confirm";
|
|
Response response = await Dio().post(
|
|
url,
|
|
data: inputData,
|
|
);
|
|
var data = Status.fromJson(response.data);
|
|
if (data.status != 'Ok') {
|
|
throw Exception(data.message);
|
|
}
|
|
}
|
|
|
|
bool isSupport() {
|
|
if (packageInfo == null || setting == null) return false;
|
|
return int.parse(packageInfo.buildNumber) >= setting.supportBuildNum;
|
|
}
|
|
|
|
Future<Setting> _getSetting() async {
|
|
var snap = await Firestore.instance
|
|
.collection(config_collection)
|
|
.document(setting_doc_id)
|
|
.get();
|
|
if (!snap.exists) {
|
|
return null;
|
|
}
|
|
_listSetting();
|
|
return Setting.fromMap(snap.data);
|
|
}
|
|
|
|
void _listSetting() {
|
|
getDocSnapshot("/configs", setting_doc_id).listen((snap) {
|
|
this.setting = Setting.fromMap(snap.data);
|
|
notifyListeners();
|
|
});
|
|
}
|
|
|
|
Future<void> updateProfile(String name) async {
|
|
await requestAPI("/user", "PUT",
|
|
payload: {"user_name": name}, token: await getToken());
|
|
}
|
|
|
|
Future<void> updateTerms(String terms) async {
|
|
await requestAPI("/terms", "PUT",
|
|
payload: {"terms": terms}, token: await getToken());
|
|
}
|
|
|
|
Future<void> agreeTerms() async {
|
|
await requestAPI("/user/agree", "PUT", token: await getToken());
|
|
}
|
|
|
|
Future<void> updateContact(Setting setting) async {
|
|
await requestAPI("/contact", "PUT",
|
|
payload: {
|
|
'email': setting.email,
|
|
'facebook_url': setting.facebook,
|
|
'web_url': setting.website,
|
|
'phones': setting.phones,
|
|
'bank_account_info': setting.bankAccountInfo,
|
|
'delivery_phone': setting.deliveryPhone,
|
|
'address': setting.address,
|
|
},
|
|
token: await getToken());
|
|
}
|
|
|
|
Future<void> updateSetting(Setting setting) async {
|
|
await requestAPI("/setting", "PUT",
|
|
payload: {
|
|
'do_expire_hours': setting.doExpireInHours,
|
|
'po_expire_hours': setting.poExpireInHours,
|
|
'po_open_at': setting.poOpenAt,
|
|
'po_close_at': setting.poCloseAt,
|
|
'po_close_on': setting.poCloseOn,
|
|
'first_storage_charge_in': setting.firstStorageChargeIn,
|
|
'first_storage_charge': setting.firstStorageCharge,
|
|
'second_storage_charge_in': setting.secondStorageChargeIn,
|
|
'second_storage_charge': setting.secondStorageCharge,
|
|
'latest_delivery_days': setting.latestDeliveryDay,
|
|
},
|
|
token: await getToken());
|
|
}
|
|
|
|
Future<void> addBankAccount(BankAccount bankAccount, File image) async {
|
|
String url = await uploadStorage(bank_images_path, image);
|
|
bankAccount.bankLogo = url;
|
|
|
|
await requestAPI("/bank_accounts", "POST",
|
|
payload: bankAccount.toMap(), token: await getToken());
|
|
}
|
|
|
|
Future<void> updateBankAccount(BankAccount bankAccount, File image) async {
|
|
if (image != null) {
|
|
String url = await uploadStorage(bank_images_path, image);
|
|
bankAccount.bankLogo = url;
|
|
}
|
|
|
|
await requestAPI("/bank_accounts", "PUT",
|
|
payload: bankAccount.toMap(), token: await getToken());
|
|
}
|
|
|
|
Future<void> deleteBankAccount(BankAccount bankAccount) async {
|
|
await requestAPI("/bank_accounts", "DELETE",
|
|
payload: bankAccount.toMap(), token: await getToken());
|
|
}
|
|
}
|