2020-08-27 22:32:40 +06:30
|
|
|
import 'dart:async';
|
|
|
|
|
|
2020-09-04 01:42:58 +06:30
|
|
|
import 'package:cloud_firestore/cloud_firestore.dart';
|
2020-10-07 02:33:06 +06:30
|
|
|
import 'package:fcs/domain/constants.dart';
|
|
|
|
|
import 'package:fcs/domain/entities/auth_result.dart' as fcs;
|
|
|
|
|
import 'package:fcs/domain/entities/auth_status.dart';
|
|
|
|
|
import 'package:fcs/domain/entities/setting.dart';
|
|
|
|
|
import 'package:fcs/domain/entities/user.dart';
|
|
|
|
|
import 'package:fcs/domain/exceiptions/signin_exception.dart';
|
|
|
|
|
import 'package:fcs/helpers/api_helper.dart';
|
2020-10-17 01:40:24 +06:30
|
|
|
import 'package:fcs/helpers/firebase_helper.dart';
|
2020-08-27 22:32:40 +06:30
|
|
|
import 'package:firebase_auth/firebase_auth.dart';
|
2020-09-13 21:49:39 +06:30
|
|
|
import 'package:logging/logging.dart';
|
2020-08-27 22:32:40 +06:30
|
|
|
|
|
|
|
|
class AuthFb {
|
2020-09-13 21:49:39 +06:30
|
|
|
final log = Logger('AuthFb');
|
|
|
|
|
|
2020-08-27 22:32:40 +06:30
|
|
|
static final AuthFb instance = AuthFb._();
|
|
|
|
|
AuthFb._();
|
|
|
|
|
|
2020-09-22 03:52:48 +06:30
|
|
|
StreamController<User> controller;
|
2020-08-30 21:26:37 +06:30
|
|
|
static final FirebaseAuth _fb = FirebaseAuth.instance;
|
2020-08-27 22:32:40 +06:30
|
|
|
static String _verificationId;
|
|
|
|
|
|
2020-09-04 01:42:58 +06:30
|
|
|
Future<fcs.AuthResult> sendSmsCodeToPhoneNumber(String phoneNumber) {
|
|
|
|
|
Completer<fcs.AuthResult> completer = Completer();
|
2020-09-13 21:49:39 +06:30
|
|
|
bool codeSentCompleted = false;
|
2020-08-27 22:32:40 +06:30
|
|
|
|
|
|
|
|
final PhoneVerificationCompleted verificationCompleted =
|
2020-09-04 01:42:58 +06:30
|
|
|
(AuthCredential credential) async {
|
2020-09-13 21:49:39 +06:30
|
|
|
AuthResult _authResult;
|
|
|
|
|
try {
|
|
|
|
|
_authResult = await _fb.signInWithCredential(credential);
|
|
|
|
|
print("PhoneVerificationCompleted :$_authResult");
|
|
|
|
|
if (_authResult == null) {
|
|
|
|
|
throw SigninException("Sigin error!");
|
|
|
|
|
}
|
|
|
|
|
} catch (e) {
|
|
|
|
|
print("Exception:$e");
|
|
|
|
|
// throw e;
|
|
|
|
|
completer.completeError(SigninException(e.toString()));
|
|
|
|
|
return;
|
2020-09-04 01:42:58 +06:30
|
|
|
}
|
|
|
|
|
fcs.AuthResult auth =
|
|
|
|
|
fcs.AuthResult(authStatus: AuthStatus.AUTH_VERIFIED);
|
2020-08-30 21:26:37 +06:30
|
|
|
completer.complete(auth);
|
2020-08-27 22:32:40 +06:30
|
|
|
print(
|
2020-09-04 01:42:58 +06:30
|
|
|
'Inside _sendCodeToPhoneNumber: signInWithPhoneNumber auto succeeded: ${_authResult.user}');
|
2020-08-27 22:32:40 +06:30
|
|
|
};
|
|
|
|
|
|
|
|
|
|
final PhoneVerificationFailed verificationFailed =
|
|
|
|
|
(AuthException authException) async {
|
|
|
|
|
print(
|
|
|
|
|
'Phone number verification failed. Code: ${authException.code}. Message: ${authException.message}');
|
2020-09-13 21:49:39 +06:30
|
|
|
completer.completeError(SigninException(
|
|
|
|
|
"Phone number verification failed:${authException.message}"));
|
2020-08-27 22:32:40 +06:30
|
|
|
};
|
|
|
|
|
|
|
|
|
|
final PhoneCodeSent codeSent =
|
|
|
|
|
(String verificationId, [int forceResendingToken]) async {
|
|
|
|
|
_verificationId = verificationId;
|
2020-09-13 21:49:39 +06:30
|
|
|
print("codeSent " + phoneNumber);
|
|
|
|
|
codeSentCompleted = true;
|
2020-09-16 02:29:50 +06:30
|
|
|
completer.complete(fcs.AuthResult(authStatus: AuthStatus.SMS_SENT));
|
2020-08-27 22:32:40 +06:30
|
|
|
};
|
|
|
|
|
|
|
|
|
|
final PhoneCodeAutoRetrievalTimeout codeAutoRetrievalTimeout =
|
|
|
|
|
(String verificationId) {
|
2020-09-13 21:49:39 +06:30
|
|
|
print("codeAutoRetrievalTimeout $verificationId ");
|
|
|
|
|
|
2020-08-27 22:32:40 +06:30
|
|
|
_verificationId = verificationId;
|
2020-09-13 21:49:39 +06:30
|
|
|
if (codeSentCompleted) {
|
|
|
|
|
completer.complete(fcs.AuthResult(authStatus: AuthStatus.SMS_SENT));
|
|
|
|
|
} else {
|
|
|
|
|
completer.completeError(SigninException("SMS code failed"));
|
|
|
|
|
}
|
2020-08-27 22:32:40 +06:30
|
|
|
};
|
|
|
|
|
|
|
|
|
|
_fb.verifyPhoneNumber(
|
|
|
|
|
phoneNumber: phoneNumber,
|
2020-09-13 21:49:39 +06:30
|
|
|
timeout: const Duration(seconds: 0),
|
2020-08-27 22:32:40 +06:30
|
|
|
verificationCompleted: verificationCompleted,
|
|
|
|
|
verificationFailed: verificationFailed,
|
|
|
|
|
codeSent: codeSent,
|
|
|
|
|
codeAutoRetrievalTimeout: codeAutoRetrievalTimeout);
|
|
|
|
|
|
|
|
|
|
return completer.future;
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-04 01:42:58 +06:30
|
|
|
Future<fcs.AuthResult> signInWithPhoneNumber(String smsCode) async {
|
2020-08-27 22:32:40 +06:30
|
|
|
try {
|
|
|
|
|
final AuthCredential credential = PhoneAuthProvider.getCredential(
|
|
|
|
|
verificationId: _verificationId,
|
|
|
|
|
smsCode: smsCode,
|
|
|
|
|
);
|
2020-09-04 01:42:58 +06:30
|
|
|
AuthResult _authResult = await _fb.signInWithCredential(credential);
|
|
|
|
|
if (_authResult == null) {
|
|
|
|
|
throw SigninException("Sigin error!");
|
|
|
|
|
}
|
2020-09-22 03:52:48 +06:30
|
|
|
await _addUserToStream(refreshIdToken: true);
|
2020-08-27 22:32:40 +06:30
|
|
|
} on Exception catch (e) {
|
2020-09-04 01:42:58 +06:30
|
|
|
return Future.error(SigninException(e.toString()));
|
2020-08-27 22:32:40 +06:30
|
|
|
}
|
2020-09-04 01:42:58 +06:30
|
|
|
return Future.value(fcs.AuthResult(authStatus: AuthStatus.AUTH_VERIFIED));
|
2020-08-27 22:32:40 +06:30
|
|
|
}
|
|
|
|
|
|
2020-09-22 03:52:48 +06:30
|
|
|
Future<void> signout() async {
|
|
|
|
|
if (userListener != null) await userListener.cancel();
|
2020-08-27 22:32:40 +06:30
|
|
|
return _fb.signOut();
|
|
|
|
|
}
|
2020-08-30 21:26:37 +06:30
|
|
|
|
2020-09-22 03:52:48 +06:30
|
|
|
Future<void> _addUserToStream({bool refreshIdToken = false}) async {
|
2020-09-04 01:42:58 +06:30
|
|
|
FirebaseUser firebaseUser = await _fb.currentUser();
|
|
|
|
|
if (firebaseUser == null) return null;
|
2020-10-17 01:40:24 +06:30
|
|
|
Map claims = await getClaims(refreshIdToken: refreshIdToken);
|
2020-09-13 21:49:39 +06:30
|
|
|
|
2020-10-17 01:40:24 +06:30
|
|
|
log.info("Claims:$claims");
|
2020-09-13 21:49:39 +06:30
|
|
|
|
2020-10-17 01:40:24 +06:30
|
|
|
String cid = claims["cid"];
|
2020-09-20 05:34:49 +06:30
|
|
|
User user;
|
|
|
|
|
if (cid != null && cid != "") {
|
2020-09-22 03:52:48 +06:30
|
|
|
user = await _getUserFromFirestore(cid);
|
2020-09-20 05:34:49 +06:30
|
|
|
}
|
|
|
|
|
if (user == null) {
|
2020-09-22 03:52:48 +06:30
|
|
|
controller.add(null);
|
|
|
|
|
return;
|
2020-09-20 05:34:49 +06:30
|
|
|
}
|
2020-09-11 16:14:36 +06:30
|
|
|
|
|
|
|
|
// add privileges
|
2020-10-17 01:40:24 +06:30
|
|
|
String privileges = claims["pr"];
|
2020-09-11 16:14:36 +06:30
|
|
|
if (privileges != null && privileges != "") {
|
|
|
|
|
user.privileges = privileges.split(":").toList();
|
2020-10-15 03:06:13 +06:30
|
|
|
} else {
|
|
|
|
|
user.privileges = [];
|
2020-09-11 16:14:36 +06:30
|
|
|
}
|
2020-09-22 03:52:48 +06:30
|
|
|
controller.add(user);
|
2020-08-30 21:26:37 +06:30
|
|
|
}
|
|
|
|
|
|
2020-09-22 03:52:48 +06:30
|
|
|
Future<User> _getUserFromFirestore(String userID) async {
|
2020-09-13 21:49:39 +06:30
|
|
|
DocumentSnapshot snap = await Firestore.instance
|
|
|
|
|
.collection(user_collection)
|
|
|
|
|
.document(userID)
|
|
|
|
|
.get();
|
|
|
|
|
if (snap.exists) {
|
|
|
|
|
User user = User.fromMap(snap.data, snap.documentID);
|
|
|
|
|
return user;
|
|
|
|
|
}
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-04 01:42:58 +06:30
|
|
|
Future<bool> isLogin() async {
|
|
|
|
|
final FirebaseUser firebaseUser = await _fb.currentUser();
|
|
|
|
|
return Future.value(firebaseUser != null);
|
2020-08-30 21:26:37 +06:30
|
|
|
}
|
|
|
|
|
|
2020-09-22 03:52:48 +06:30
|
|
|
Future<void> signup(String userName) async {
|
2020-08-30 21:26:37 +06:30
|
|
|
await requestAPI("/signup", "POST",
|
|
|
|
|
payload: {
|
2020-09-04 01:42:58 +06:30
|
|
|
'user_name': userName,
|
2020-08-30 21:26:37 +06:30
|
|
|
},
|
|
|
|
|
token: await getToken());
|
2020-09-22 03:52:48 +06:30
|
|
|
await _addUserToStream(refreshIdToken: true);
|
|
|
|
|
_startUserListener();
|
|
|
|
|
return;
|
2020-08-30 21:26:37 +06:30
|
|
|
}
|
|
|
|
|
|
2020-09-22 03:52:48 +06:30
|
|
|
Future<void> joinInvite(String userName) async {
|
2020-09-17 06:02:48 +06:30
|
|
|
await requestAPI("/join_invite", "POST",
|
|
|
|
|
payload: {
|
|
|
|
|
'user_name': userName,
|
|
|
|
|
},
|
|
|
|
|
token: await getToken());
|
|
|
|
|
// refresh token once signup
|
2020-09-22 03:52:48 +06:30
|
|
|
await _addUserToStream(refreshIdToken: true);
|
|
|
|
|
_startUserListener();
|
|
|
|
|
return;
|
2020-09-17 06:02:48 +06:30
|
|
|
}
|
|
|
|
|
|
2020-09-13 21:49:39 +06:30
|
|
|
Future<bool> hasInvite() async {
|
|
|
|
|
var invited =
|
|
|
|
|
await requestAPI("/check_invitation", "GET", token: await getToken());
|
|
|
|
|
return invited["invited"];
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-11 02:17:23 +06:30
|
|
|
Future<void> updateProfileName(String newUserName) async {
|
2020-09-13 21:49:39 +06:30
|
|
|
return await requestAPI("/profile", "PUT",
|
|
|
|
|
payload: {"user_name": newUserName}, token: await getToken());
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-11 02:17:23 +06:30
|
|
|
Future<void> updatePreferredCurrency(String currency) async {
|
|
|
|
|
return await requestAPI("/currency", "PUT",
|
|
|
|
|
payload: {"preferred_currency": currency}, token: await getToken());
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-07 16:05:28 +06:30
|
|
|
Stream<Setting> settings() async* {
|
|
|
|
|
Stream<DocumentSnapshot> snapshot = Firestore.instance
|
|
|
|
|
.collection(config_collection)
|
|
|
|
|
.document(setting_doc_id)
|
|
|
|
|
.snapshots();
|
|
|
|
|
|
|
|
|
|
await for (var snap in snapshot) {
|
|
|
|
|
Setting setting = Setting.fromMap(snap.data);
|
|
|
|
|
yield setting;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-09-17 06:02:48 +06:30
|
|
|
|
2020-09-22 03:52:48 +06:30
|
|
|
Future<String> _getCurrentUserID() async {
|
|
|
|
|
FirebaseUser firebaseUser = await _fb.currentUser();
|
|
|
|
|
if (firebaseUser == null) return null;
|
2020-10-17 01:40:24 +06:30
|
|
|
Map claims = await getClaims();
|
|
|
|
|
String cid = claims["cid"];
|
2020-09-22 03:52:48 +06:30
|
|
|
return cid;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Future<void> _startUserListener() async {
|
|
|
|
|
if (userListener != null) userListener.cancel();
|
|
|
|
|
String _userID = await _getCurrentUserID();
|
|
|
|
|
if (_userID == null) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-17 06:02:48 +06:30
|
|
|
Stream<DocumentSnapshot> snapshot = Firestore.instance
|
|
|
|
|
.collection(user_collection)
|
2020-09-22 03:52:48 +06:30
|
|
|
.document(_userID)
|
2020-09-17 06:02:48 +06:30
|
|
|
.snapshots();
|
2020-09-22 03:52:48 +06:30
|
|
|
userListener = snapshot.listen((snap) async {
|
2020-09-17 06:02:48 +06:30
|
|
|
User user = User.fromMap(snap.data, snap.documentID);
|
2020-09-22 03:52:48 +06:30
|
|
|
|
|
|
|
|
FirebaseUser firebaseUser = await _fb.currentUser();
|
|
|
|
|
if (firebaseUser == null) {
|
|
|
|
|
userListener.cancel();
|
|
|
|
|
return;
|
|
|
|
|
}
|
2020-10-17 01:40:24 +06:30
|
|
|
try {
|
|
|
|
|
// 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);
|
|
|
|
|
} catch (e) {
|
|
|
|
|
controller.add(null);
|
2020-09-22 03:52:48 +06:30
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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();
|
|
|
|
|
}
|
2020-09-17 06:02:48 +06:30
|
|
|
}
|
2020-09-22 03:52:48 +06:30
|
|
|
|
|
|
|
|
controller = StreamController<User>(
|
|
|
|
|
onListen: _start, onPause: _stop, onResume: _start, onCancel: _stop);
|
|
|
|
|
|
|
|
|
|
return controller.stream;
|
2020-09-17 06:02:48 +06:30
|
|
|
}
|
2020-08-27 22:32:40 +06:30
|
|
|
}
|