Files
fcs/lib/model/main_model.dart
2020-05-29 07:45:27 +06:30

451 lines
12 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: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 '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;
Setting setting;
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();
// });
}
resetPinTimer() {
if (pinTimer != null && pinTimer.isActive) {
pinTimer.cancel();
}
pinRequired = false;
pinTimer = Timer(Duration(minutes: PIN_TIME_MIN), () {
pinRequired = true;
});
}
bool isLogin() {
return true;
}
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);
model.mainModel = this;
}
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;
}
Future<void> _logUser(FirebaseUser firebaseUser) async {
IdTokenResult idtoken = await firebaseUser.getIdToken();
await requestAPI(
"/log",
"GET",
token: idtoken.token,
);
}
Future<void> logout() async {
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());
}
}