This commit is contained in:
Sai Naw Wun
2020-09-22 03:52:48 +06:30
parent 139fc8e7d3
commit 01a2798a74
19 changed files with 229 additions and 206 deletions

View File

@@ -108,7 +108,6 @@ class _AppState extends State<App> {
..addModel(packageModel)
..addModel(messageModel)
..addModel(marketModel);
mainModel2.init();
_newLocaleDelegate = AppTranslationsDelegate(newLocale: null);
Translation().onLocaleChanged = onLocaleChange;

View File

@@ -18,6 +18,7 @@ class AuthFb {
static final AuthFb instance = AuthFb._();
AuthFb._();
StreamController<User> controller;
static final FirebaseAuth _fb = FirebaseAuth.instance;
static String _verificationId;
@@ -97,6 +98,7 @@ class AuthFb {
if (_authResult == null) {
throw SigninException("Sigin error!");
}
await _addUserToStream(refreshIdToken: true);
} on Exception catch (e) {
return Future.error(SigninException(e.toString()));
}
@@ -104,20 +106,12 @@ class AuthFb {
return Future.value(fcs.AuthResult(authStatus: AuthStatus.AUTH_VERIFIED));
}
Future<void> signout() {
Future<void> signout() async {
if (userListener != null) await userListener.cancel();
return _fb.signOut();
}
Stream<User> get onAuthStatus async* {
await for (FirebaseUser firebaseUser in _fb.onAuthStateChanged) {
if (firebaseUser == null) {
yield null;
}
yield await getUser();
}
}
Future<User> getUser({bool refreshIdToken = false}) async {
Future<void> _addUserToStream({bool refreshIdToken = false}) async {
FirebaseUser firebaseUser = await _fb.currentUser();
if (firebaseUser == null) return null;
IdTokenResult idToken =
@@ -128,13 +122,11 @@ class AuthFb {
String cid = idToken.claims["cid"];
User user;
if (cid != null && cid != "") {
user = await getUserFromFirestore(cid);
user = await _getUserFromFirestore(cid);
}
if (user == null) {
user = User();
user.id = cid;
user.phoneNumber = firebaseUser.phoneNumber;
user.status = idToken.claims["st"];
controller.add(null);
return;
}
// add privileges
@@ -142,11 +134,10 @@ class AuthFb {
if (privileges != null && privileges != "") {
user.privileges = privileges.split(":").toList();
}
return user;
controller.add(user);
}
Future<User> getUserFromFirestore(String userID) async {
Future<User> _getUserFromFirestore(String userID) async {
DocumentSnapshot snap = await Firestore.instance
.collection(user_collection)
.document(userID)
@@ -163,24 +154,27 @@ class AuthFb {
return Future.value(firebaseUser != null);
}
Future<User> signup(String userName) async {
Future<void> signup(String userName) async {
await requestAPI("/signup", "POST",
payload: {
'user_name': userName,
},
token: await getToken());
// refresh token once signup
return getUser(refreshIdToken: true);
await _addUserToStream(refreshIdToken: true);
_startUserListener();
return;
}
Future<User> joinInvite(String userName) async {
Future<void> joinInvite(String userName) async {
await requestAPI("/join_invite", "POST",
payload: {
'user_name': userName,
},
token: await getToken());
// refresh token once signup
return getUser(refreshIdToken: true);
await _addUserToStream(refreshIdToken: true);
_startUserListener();
return;
}
Future<bool> hasInvite() async {
@@ -224,16 +218,72 @@ class AuthFb {
}
}
Stream<User> user(String userID) async* {
Future<String> _getCurrentUserID() async {
FirebaseUser firebaseUser = await _fb.currentUser();
if (firebaseUser == null) return null;
IdTokenResult idToken = await firebaseUser.getIdToken();
String cid = idToken.claims["cid"];
return cid;
}
Future<void> _startUserListener() async {
if (userListener != null) userListener.cancel();
String _userID = await _getCurrentUserID();
if (_userID == null) {
return;
}
Stream<DocumentSnapshot> snapshot = Firestore.instance
.collection(user_collection)
.document(userID)
.document(_userID)
.snapshots();
await for (var snap in snapshot) {
userListener = snapshot.listen((snap) async {
User user = User.fromMap(snap.data, snap.documentID);
user = await getUser(refreshIdToken: true);
yield user;
FirebaseUser firebaseUser = await _fb.currentUser();
if (firebaseUser == null) {
userListener.cancel();
return;
}
// get privilege from claim
IdTokenResult idToken = await firebaseUser.getIdToken(refresh: true);
String privileges = idToken.claims["pr"];
if (privileges != null && privileges != "") {
user.privileges = privileges.split(":").toList();
}
controller.add(user);
});
}
StreamSubscription<DocumentSnapshot> userListener;
Stream<User> user() {
// ignore: close_sinks
StreamSubscription<FirebaseUser> authListener;
Future<void> _start() async {
authListener = _fb.onAuthStateChanged.listen((firebaseUser) async {
if (firebaseUser == null) {
controller.add(null);
} else {
_addUserToStream(refreshIdToken: true);
_startUserListener();
}
});
}
void _stop() {
if (userListener != null) {
userListener.cancel();
}
if (authListener != null) {
authListener.cancel();
}
}
controller = StreamController<User>(
onListen: _start, onPause: _stop, onResume: _start, onCancel: _stop);
return controller.stream;
}
}

View File

@@ -1,6 +1,7 @@
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:fcs/fcs/common/helpers/const.dart';
import 'package:fcs/fcs/common/pages/package/package_info.dart';
import 'package:flutter/foundation.dart';
import 'package:intl/intl.dart';
DateFormat dayFormat = DateFormat("MMM dd yyyy");
@@ -108,6 +109,12 @@ class User {
lastMessageTime: _date == null ? null : _date.toDate());
}
bool diffPrivileges(User another) {
another.privileges.sort((a, b) => a.compareTo(b));
privileges.sort((a, b) => a.compareTo(b));
return !listEquals(another.privileges, privileges);
}
bool isCustomer() {
return privileges == null || privileges.length == 0;
}
@@ -147,6 +154,6 @@ class User {
@override
String toString() {
return 'User{name: $name, phoneNumber: $phoneNumber,status:$status}';
return 'User{id:$id, name: $name, phoneNumber: $phoneNumber,status:$status}';
}
}

View File

@@ -12,10 +12,6 @@ class MessageModel extends BaseModel {
List<Message> messages;
void initUser(user) {
super.initUser(user);
}
@override
logout() async {
if (listener != null) await listener.cancel();

View File

@@ -15,8 +15,9 @@ class CustomerModel extends BaseModel {
StreamSubscription<QuerySnapshot> customerListener;
StreamSubscription<QuerySnapshot> invitationListener;
void initUser(user) async {
super.initUser(user);
@override
void privilegeChanged() {
super.privilegeChanged();
_loadCustomer();
_loadInvitations();
}

View File

@@ -29,7 +29,6 @@ class FAQModel extends BaseModel {
snaps.documents.forEach((d) {
faqs.add(FAQ.fromMap(d.data, d.documentID));
});
print("in listener");
notifyListeners();
});
} catch (e) {

View File

@@ -12,19 +12,11 @@ class MarketModel extends BaseModel {
StreamSubscription<QuerySnapshot> listener;
List<Market> markets = [];
void initUser(user) async {
super.initUser(user);
MarketModel() {
_loadMarkets();
}
@override
logout() async {
markets = [];
}
Future<void> _loadMarkets() async {
if (user == null || !user.hasStaffs()) return;
try {
if (listener != null) listener.cancel();

View File

@@ -14,13 +14,13 @@ abstract class BaseModel extends ChangeNotifier {
this.user = user;
}
void privilegeChanged() {}
void initSetting(Setting setting) async {
this.setting = setting;
}
void logout(){
}
void logout() {}
// request makes http request
// if token is null

View File

@@ -1,8 +1,6 @@
import 'dart:async';
import 'dart:developer';
import 'package:fcs/fcs/common/domain/entities/auth_result.dart';
import 'package:fcs/fcs/common/domain/entities/auth_status.dart';
import 'package:fcs/fcs/common/domain/entities/setting.dart';
import 'package:fcs/fcs/common/domain/entities/user.dart';
import 'package:fcs/fcs/common/helpers/network_connectivity.dart';
@@ -36,20 +34,11 @@ class MainModel extends ChangeNotifier {
NetworkConnectivity.instance.statusStream.listen((data) {
bool _isOnline = data["isOnline"];
if (_isOnline && !this.isOnline) {
init();
_init();
}
this.isOnline = _isOnline;
notifyListeners();
});
Services.instance.authService.onAuthStatus().listen((event) async {
this.user =
await Services.instance.authService.getUser(refreshIdToken: true);
_initUser(user);
if (user != null) {
uploadMsgToken();
}
notifyListeners();
});
}
bool faqEditable() {
@@ -84,78 +73,51 @@ class MainModel extends ChangeNotifier {
return this.user != null && this.user.hasAdmin();
}
init() async {
await _loadSetting();
// userListener should never be closed
StreamSubscription<User> userListener;
_init() async {
await _listenSetting();
this.isFirstLaunch = await SharedPref.isFirstLaunch();
this.isFirstLaunch = this.isFirstLaunch ?? true;
// _loadUser();
this.packageInfo = await PackageInfo.fromPlatform();
if (userListener != null) userListener.cancel();
userListener =
Services.instance.authService.getUserStream().listen((_user) {
if (_user != null) {
models.forEach((m) => m.initUser(_user));
// call diffPrivileges if privilege changed or first time login
if (this.user == null || _user.diffPrivileges(this.user)) {
models.forEach((m) => m.privilegeChanged());
}
if (this.user == null) {
uploadMsgToken();
}
} else {
if (this.user != null) {
models.forEach((m) => m.logout());
}
}
this.user = _user;
isLoaded = true;
notifyListeners();
});
}
void addModel(BaseModel model) {
models.add(model);
}
StreamSubscription<User> userListener;
void _initUser(User user) {
if (user != null) {
if (user.id != null && user.id != "") {
if (userListener != null) userListener.cancel();
userListener = Services.instance.authService
.getUserStream(user.id)
.listen((event) {
this.user = event;
models.forEach((m) => m.initUser(user));
notifyListeners();
});
}
models.forEach((m) => m.initUser(user));
} else {
models.forEach((m) => m.logout());
}
isLoaded = true;
// if (firebaseMessaging != null) {
// firebaseMessaging.subscribeToTopic(user.docID);
// }
}
void _initSetting(Setting setting) {
models.forEach((m) => m.initSetting(setting));
}
Future<void> _loadSetting() async {
Future<void> _listenSetting() async {
try {
Services.instance.authService.getSetting().listen((event) {
this.setting = event;
_initSetting(setting);
models.forEach((m) => m.initSetting(setting));
notifyListeners();
});
} finally {}
}
// void _loadUser() async {
// try {
// this.user = await Services.instance.authService.getUser();
// _initUser(user);
// } finally {
// this.isLoaded = true;
// notifyListeners();
// }
// }
@override
void dispose() {
super.dispose();
// if (this.userListener != null) {
// this.userListener.cancel();
// }
// SharedPref.removeUser();
// this.user = User();
}
bool isSupport() {
if (packageInfo == null || setting == null) return false;
return int.parse(packageInfo.buildNumber) >= setting.supportBuildNum;
@@ -168,11 +130,6 @@ class MainModel extends ChangeNotifier {
Future<AuthResult> signin(String smsCode) async {
AuthResult authResult =
await Services.instance.authService.signInWithSmsCode(smsCode);
if (authResult != null &&
authResult.authStatus == AuthStatus.AUTH_VERIFIED) {
this.user = await Services.instance.authService.getUser();
uploadMsgToken();
}
return authResult;
}
@@ -187,13 +144,9 @@ class MainModel extends ChangeNotifier {
}
Future<void> signout() async {
// logout models
models.forEach((m) => m.logout());
await removeMsgToken();
this.user = null;
notifyListeners();
return Services.instance.authService.signout();
await Services.instance.authService.signout();
models.forEach((m) => m.logout());
}
Future<bool> hasInvite() async {
@@ -202,22 +155,15 @@ class MainModel extends ChangeNotifier {
Future<void> signup(String userName) async {
await Services.instance.authService.signup(userName);
this.user =
await Services.instance.authService.getUser(refreshIdToken: true);
notifyListeners();
}
Future<void> joinInvite(String userName) async {
await Services.instance.authService.joinInvite(userName);
this.user =
await Services.instance.authService.getUser(refreshIdToken: true);
notifyListeners();
}
Future<void> updateProfile(String newUserName) async {
await Services.instance.authService.updateProfile(newUserName);
this.user =
await Services.instance.authService.getUser(refreshIdToken: true);
notifyListeners();
}
}

View File

@@ -17,8 +17,9 @@ class PackageModel extends BaseModel {
StreamSubscription<QuerySnapshot> listener;
List<Package> packages = [];
void initUser(user) {
super.initUser(user);
@override
void privilegeChanged() {
super.privilegeChanged();
_loadPackages();
}

View File

@@ -321,9 +321,11 @@ class _ProfileState extends State<Profile> {
setState(() {
_isLoading = true;
});
try {
await context.read<MainModel>().signout();
Navigator.of(context)
.pushNamedAndRemoveUntil("/welcome", ModalRoute.withName('/welcome'));
Navigator.of(context).pushNamedAndRemoveUntil(
"/welcome", ModalRoute.withName('/welcome'));
} catch (e) {} finally {
Future.delayed(Duration(seconds: 1), () {
if (mounted) {
setState(() {
@@ -331,6 +333,7 @@ class _ProfileState extends State<Profile> {
});
}
});
}
});
}
}

View File

@@ -11,9 +11,9 @@ navigateAfterAuthVerified(BuildContext context) async {
User user = Provider.of<MainModel>(context, listen: false).user;
Setting setting = Provider.of<MainModel>(context, listen: false).setting;
if (user == null || setting == null) return;
if (setting == null) return;
if (user.joined || user.requested) {
if (user != null && (user.joined || user.requested)) {
Navigator.pushNamedAndRemoveUntil(context, "/home", (r) => false);
} else {
if (setting.inviteRequired) {

View File

@@ -17,8 +17,9 @@ class StaffModel extends BaseModel {
List<User> employees = [];
List<Privilege> privileges = [];
void initUser(user) async {
super.initUser(user);
@override
void privilegeChanged() {
super.privilegeChanged();
_loadPrivileges();
_loadEmployees();
}

View File

@@ -2,6 +2,8 @@ import 'dart:io';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:fcs/fcs/common/helpers/theme.dart';
import 'package:fcs/fcs/common/pages/widgets/bottom_up_page_route.dart';
import 'package:fcs/fcs/common/pages/widgets/right_left_page_rout.dart';
import 'package:fcs/fcs/common/pages/widgets/show_img.dart';
import 'package:fcs/fcs/common/pages/widgets/show_multiple_img.dart';
import 'package:flutter/cupertino.dart';
@@ -93,12 +95,10 @@ class _MultiImageFileState extends State<MultiImageFile> {
return InkWell(
onTap: () => Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ShowMultiImage(
RightLeftPageRoute(ShowMultiImage(
displayImageSources: fileContainers,
initialPage: index,
)),
),
))),
child: Stack(alignment: Alignment.topLeft, children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),

View File

@@ -0,0 +1,23 @@
import 'package:flutter/cupertino.dart';
class RightLeftPageRoute extends PageRouteBuilder {
final Widget child;
RightLeftPageRoute(this.child)
: super(
pageBuilder: (context, animation, secondaryAnimation) => child,
transitionsBuilder: (context, animation, secondaryAnimation, child) {
var begin = Offset(1.0, 0.0);
var end = Offset.zero;
var curve = Curves.ease;
var tween =
Tween(begin: begin, end: end).chain(CurveTween(curve: curve));
return SlideTransition(
position: animation.drive(tween),
child: child,
);
},
);
}

View File

@@ -24,14 +24,27 @@ class _ShowMultiImageState extends State<ShowMultiImage> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
return SafeArea(
child: Scaffold(
backgroundColor: primaryColor,
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
IconButton(
onPressed: () => Navigator.pop(context),
icon: Icon(
Icons.close,
color: Colors.white,
),
),
Expanded(
child: PhotoViewGallery.builder(
scrollPhysics: const BouncingScrollPhysics(),
builder: (BuildContext context, int index) {
return PhotoViewGalleryPageOptions(
imageProvider: widget.displayImageSources[index].imageProvider,
initialScale: PhotoViewComputedScale.contained * 0.8,
imageProvider:
widget.displayImageSources[index].imageProvider,
initialScale: PhotoViewComputedScale.contained * 0.95,
heroAttributes: PhotoViewHeroAttributes(
tag: widget.displayImageSources[index].hashCode),
);
@@ -44,7 +57,8 @@ class _ShowMultiImageState extends State<ShowMultiImage> {
child: CircularProgressIndicator(
value: event == null
? 0
: event.cumulativeBytesLoaded / event.expectedTotalBytes,
: event.cumulativeBytesLoaded /
event.expectedTotalBytes,
),
),
),
@@ -52,6 +66,9 @@ class _ShowMultiImageState extends State<ShowMultiImage> {
color: primaryColor,
),
pageController: pageController,
)));
)),
],
)),
);
}
}

View File

@@ -33,13 +33,8 @@ class AuthServiceImp implements AuthService {
}
@override
Future<User> getUser({bool refreshIdToken = false}) {
return authFb.getUser(refreshIdToken: refreshIdToken);
}
@override
Stream<User> getUserStream(String userID) {
return authFb.user(userID);
Stream<User> getUserStream() {
return authFb.user();
}
@override
@@ -48,7 +43,7 @@ class AuthServiceImp implements AuthService {
}
@override
Future<User> signup(String userName) {
Future<void> signup(String userName) {
return authFb.signup(userName);
}
@@ -57,11 +52,6 @@ class AuthServiceImp implements AuthService {
return authFb.joinInvite(userName);
}
@override
Stream<User> onAuthStatus() {
return authFb.onAuthStatus;
}
@override
Future<String> getToken() {
return authFb.getToken();

View File

@@ -6,13 +6,11 @@ abstract class AuthService {
Future<AuthResult> sendSmsCodeToPhoneNumber(String phoneNumber);
Future<AuthResult> signInWithSmsCode(String smsCode);
Future<void> signout();
Future<User> getUser({bool refreshIdToken = false});
Future<User> signup(String userName);
Future<void> signup(String userName);
Future<User> joinInvite(String userName);
Future<void> updateProfile(String newUserName);
Future<bool> hasInvite();
Stream<User> getUserStream(String userID);
Stream<User> getUserStream();
Stream<Setting> getSetting();
Stream<User> onAuthStatus();
Future<String> getToken();
}

View File

@@ -81,7 +81,7 @@ class MainModel extends ChangeNotifier {
// notifyListeners();
// });
_loadFcs();
Services.instance.authService.onAuthStatus().listen((event) {});
// Services.instance.authService.onAuthStatus().listen((event) {});
}
List<PaymentMethod> get paymentMethods {