add phone signin

This commit is contained in:
Sai Naw Wun
2020-08-27 22:32:40 +06:30
parent 52078d7ee0
commit 76155a10e8
21 changed files with 543 additions and 13 deletions

1
.gitignore vendored
View File

@@ -42,3 +42,4 @@ app.*.map.json
# Exceptions to above rules. # Exceptions to above rules.
!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
android/key.properties android/key.properties
android/key.jks

16
.vscode/launch.json vendored
View File

@@ -2,7 +2,19 @@
"version": "0.2.0", "version": "0.2.0",
"configurations": [ "configurations": [
{ {
"name": "Flutter Dev", "name": "Local Dev",
"request": "launch",
"type": "dart",
"program": "lib/main-local.dart",
"args": [
"-t",
"lib/main-local.dart",
"--flavor",
"dev"
],
},
{
"name": "Dev",
"request": "launch", "request": "launch",
"type": "dart", "type": "dart",
"program": "lib/main-dev.dart", "program": "lib/main-dev.dart",
@@ -14,7 +26,7 @@
], ],
}, },
{ {
"name": "Flutter Pro", "name": "Pro",
"request": "launch", "request": "launch",
"type": "dart", "type": "dart",
"program": "lib/main-prod.dart", "program": "lib/main-prod.dart",

View File

@@ -0,0 +1,82 @@
import 'dart:async';
import 'package:fcs/fcs/common/domain/entities/auth.dart';
import 'package:fcs/fcs/common/domain/entities/auth_status.dart';
import 'package:firebase_auth/firebase_auth.dart';
class AuthFb {
static final AuthFb instance = AuthFb._();
AuthFb._();
final FirebaseAuth _fb = FirebaseAuth.instance;
static String _verificationId;
Future<Auth> sendSmsCodeToPhoneNumber(String phoneNumber) {
Completer<Auth> completer = Completer();
final PhoneVerificationCompleted verificationCompleted =
(AuthCredential user) {
completer.complete(Auth(authStatus: AuthStatus.AUTH_VERIFIED));
print(
'Inside _sendCodeToPhoneNumber: signInWithPhoneNumber auto succeeded: $user');
};
final PhoneVerificationFailed verificationFailed =
(AuthException authException) async {
print(
'Phone number verification failed. Code: ${authException.code}. Message: ${authException.message}');
completer.complete(Auth(
authStatus: AuthStatus.ERROR,
authErrorCode: authException.code,
authErrorMsg: authException.message));
throw authException;
};
final PhoneCodeSent codeSent =
(String verificationId, [int forceResendingToken]) async {
_verificationId = verificationId;
print("code sent to " + phoneNumber);
completer.complete(Auth(authStatus: AuthStatus.SMS_SENT));
};
final PhoneCodeAutoRetrievalTimeout codeAutoRetrievalTimeout =
(String verificationId) {
_verificationId = verificationId;
};
_fb.verifyPhoneNumber(
phoneNumber: phoneNumber,
timeout: const Duration(minutes: 2),
verificationCompleted: verificationCompleted,
verificationFailed: verificationFailed,
codeSent: codeSent,
codeAutoRetrievalTimeout: codeAutoRetrievalTimeout);
return completer.future;
}
Future<Auth> signInWithPhoneNumber(String smsCode) async {
Auth auth = Auth();
try {
final AuthCredential credential = PhoneAuthProvider.getCredential(
verificationId: _verificationId,
smsCode: smsCode,
);
var firebaseUser = await _fb.signInWithCredential(credential);
final FirebaseUser currentUser = await _fb.currentUser();
assert(firebaseUser.user.uid == currentUser.uid);
auth.uid = firebaseUser.user.uid;
auth.authStatus = AuthStatus.AUTH_VERIFIED;
} on Exception catch (e) {
auth.authStatus = AuthStatus.ERROR;
auth.authErrorMsg = e.toString();
}
return Future.value(auth);
}
Future<void> logout() {
return _fb.signOut();
}
}

View File

@@ -0,0 +1,7 @@
import 'package:fcs/fcs/common/domain/entities/user.dart';
class UserFBDataProvider {
Future<User> getUser(String id) {
return null;
}
}

View File

@@ -0,0 +1,7 @@
import 'package:fcs/fcs/common/domain/entities/user.dart';
class UserLocalDataProvider {
Future<User> getUser(String id) {
return null;
}
}

View File

@@ -0,0 +1,11 @@
import 'auth_status.dart';
class Auth {
AuthStatus authStatus;
String authErrorCode;
String authErrorMsg;
String uid;
Auth({this.authStatus, this.authErrorCode, this.authErrorMsg});
}

View File

@@ -0,0 +1 @@
enum AuthStatus { SMS_SENT, AUTH_VERIFIED, ERROR }

View File

@@ -0,0 +1,5 @@
class Connectivity {
get isConnected {
return true;
}
}

View File

@@ -0,0 +1,285 @@
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
class User {
String id;
String name;
String phoneNumber;
String fcsID;
String shippingAddress;
String deliveryAddress;
String get phone => phoneNumber != null && phoneNumber.startsWith("959")
? "0${phoneNumber.substring(2)}"
: phoneNumber;
List<String> claimPrivileges = [];
final String dateofBirth;
final String gender;
final String status;
final bool disable;
bool registeredBuyer;
List<String> privilegeIds;
String roleName;
String roleID;
bool agreeTerms;
String bizID;
String accountID;
String email;
bool isBlock;
int userLevel;
String userLevelID;
String frontUrl;
String backUrl;
String selfieUrl;
DateTime lastActiveTime;
String device;
String primaryDeviceID;
String primaryDeviceName;
String pin;
String get getname => this.name;
String get getphonenumber => this.phoneNumber;
String get getdateofBirth => this.dateofBirth;
bool get getdisable => this.disable;
Future<void> setFirebaseUser(FirebaseUser firebaseUser) async {
IdTokenResult idToken = await firebaseUser.getIdToken(refresh: true);
String privileges = idToken.claims["privileges"];
if (privileges == null || privileges == "") return;
this.claimPrivileges = privileges.split(":").toList();
this.accountID = idToken.claims["account_id"];
this.bizID = idToken.claims["biz_id"];
}
User(
{this.id,
this.name,
this.gender,
this.phoneNumber,
this.fcsID,
this.shippingAddress,
this.deliveryAddress,
this.dateofBirth,
this.roleName,
this.roleID,
this.privilegeIds,
this.email,
this.disable,
this.status,
this.frontUrl,
this.backUrl,
this.selfieUrl,
this.registeredBuyer,
this.agreeTerms,
this.lastActiveTime,
this.device,
this.primaryDeviceID,
this.primaryDeviceName,
this.isBlock,
this.userLevel,
this.userLevelID,
this.pin});
factory User.fromJson(Map<String, dynamic> json) {
return User(
id: json['id'],
name: json['user_name'],
phoneNumber: json['phone_number'],
dateofBirth: json['dob'],
gender: json['gender'],
frontUrl: json['front_url'],
backUrl: json['back_url'],
selfieUrl: json['selfie_url'],
status: json['status'],
agreeTerms: json['agree_terms'],
disable: json['disable'],
registeredBuyer: json['registered_buyer'],
privilegeIds: json['privileges'],
email: json['email'],
isBlock: json['black_list'],
userLevel: json['user_level'],
userLevelID: json['user_level_id'],
pin: json['pin']);
}
factory User.fromUserJson(Map<String, dynamic> json) {
DateTime parsedDate = DateTime.parse(json['last_active_time']);
return User(
id: json['id'],
name: json['user_name'],
phoneNumber: json['phone_number'],
dateofBirth: json['dob'],
roleName: json['role_name'],
roleID: json['role_id'],
disable: json['disable'],
gender: json['gender'],
status: json['status'],
lastActiveTime: parsedDate == null ? null : parsedDate,
device: json['last_active_device'],
email: json['email'],
primaryDeviceID: json['primary_device_id'],
primaryDeviceName: json['primary_device_name'],
userLevel: json['user_level'],
userLevelID: json['user_level_id'],
pin: json['pin']);
}
Map<String, dynamic> toJson() => {
'id': id,
'user_name': name,
'gender': gender,
'phone_number': phoneNumber,
'dob': dateofBirth,
'roleName': roleName,
'roleId': roleID,
'disable': disable,
'status': status,
'registered_buyer': registeredBuyer,
'agree_terms': agreeTerms,
'front_url': frontUrl,
'back_url': backUrl,
'selfie_url': selfieUrl,
'email': email,
'black_list': isBlock,
'user_level': userLevel,
'user_level_id': userLevelID,
'pin': pin,
'privileges': privilegeIds,
};
Map<String, dynamic> toMap() {
return {
'user_name': name,
'phone_number': phoneNumber,
'dob': dateofBirth,
'role_name': roleName,
'role_id': roleID,
'disable': disable,
'gender': gender,
'status': status,
'email': email,
'black_list': isBlock,
'user_level': userLevel,
'user_level_id': userLevelID,
'pin': pin
};
}
factory User.fromMap(Map<String, dynamic> map, String docID) {
var activeTime = (map['last_active_time'] as Timestamp);
return User(
id: docID,
name: map['user_name'],
phoneNumber: map['phone_number'],
privilegeIds:
map['privileges'] == null ? [] : map['privileges'].cast<String>(),
dateofBirth: map['dob'],
roleName: map['role_name'],
roleID: map['role_id'],
disable: map['disable'],
gender: map['gender'],
status: map['status'],
registeredBuyer: map['registered_buyer'],
agreeTerms: map['agree_terms'] == null ? false : map['agree_terms'],
lastActiveTime: activeTime == null ? null : activeTime.toDate(),
device: map['last_active_device'],
email: map['email'],
primaryDeviceID: map['primary_device_id'],
primaryDeviceName: map['primary_device_name'],
isBlock: map['black_list'],
userLevel: map['user_level'],
userLevelID: map['user_level_id'],
pin: map['pin']);
}
bool isBlockUser() {
return this.isBlock == true;
}
bool isPrimaryDevice() {
return this.primaryDeviceID != null && this.primaryDeviceID != '';
}
bool isRegisteredBuyer() {
return this.registeredBuyer != null && this.registeredBuyer;
}
bool isSysAdmin() {
return claimPrivileges != null
? claimPrivileges.contains('sys_admin')
: false;
}
bool isSysSupport() {
return claimPrivileges != null
? claimPrivileges.contains('sys_support')
: false;
}
bool isBizAdmin() {
return claimPrivileges != null ? claimPrivileges.contains('ba') : false;
}
bool isBuyer() {
return claimPrivileges == null || claimPrivileges.length == 0;
}
bool isEmail() {
return email != null;
}
bool hasAccount() {
return isOwner() ||
(claimPrivileges != null ? claimPrivileges.contains('a') : false);
}
bool hasDelivery() {
return isOwner() ||
(claimPrivileges != null ? claimPrivileges.contains('d') : false);
}
bool hasBuyer() {
return isOwner() ||
(claimPrivileges != null ? claimPrivileges.contains('b') : false);
}
bool isOwner() {
return claimPrivileges != null ? claimPrivileges.contains('o') : false;
}
bool isOwnerAndAbove() {
return isOwner() || isBizAdmin() || isSysAdmin();
}
bool hasAdmin() {
return isOwner() ||
(claimPrivileges != null ? claimPrivileges.contains('admin') : false);
}
bool hasDO() {
return isOwner() ||
(claimPrivileges != null ? claimPrivileges.contains('do') : false);
}
bool hasPO() {
return isOwner() ||
(claimPrivileges != null ? claimPrivileges.contains('po') : false);
}
bool hasInventory() {
return isOwner() ||
(claimPrivileges != null ? claimPrivileges.contains('inv') : false);
}
@override
String toString() {
return 'User{name: $name, phoneNumber: $phoneNumber,dateofBirth:$dateofBirth,disable:$disable,gender:$gender,roleName:$roleName,roleID:$roleID,privilegeIds:$privilegeIds,status:$status,frontUrl:$frontUrl,backUrl:$backUrl,selfieUrl:$selfieUrl}';
}
}

View File

@@ -0,0 +1,12 @@
class ServerException {
@override
List<Object> get props => null;
call() {
return null;
}
@override
bool get stringify => null;
}

View File

@@ -8,10 +8,10 @@ import 'package:flutter/material.dart';
import 'package:pin_input_text_field/pin_input_text_field.dart'; import 'package:pin_input_text_field/pin_input_text_field.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import '../theme/theme.dart'; import '../../../../theme/theme.dart';
import '../widget/local_text.dart'; import '../../../../widget/local_text.dart';
import '../widget/progress.dart'; import '../../../../widget/progress.dart';
import 'user_edit.dart'; import '../../../../pages/user_edit.dart';
const resend_count_sec = 5; const resend_count_sec = 5;

View File

@@ -0,0 +1,8 @@
import 'package:fcs/fcs/common/data/providers/auth_fb.dart';
import 'package:flutter/foundation.dart';
class SigninModel extends ChangeNotifier {
setPhoneNumber(String phoneNumber) async {
await AuthFb.instance.sendSmsCodeToPhoneNumber(phoneNumber);
}
}

View File

@@ -4,11 +4,11 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import '../theme/theme.dart'; import '../../../../theme/theme.dart';
import '../widget/local_text.dart'; import '../../../../widget/local_text.dart';
import '../widget/progress.dart'; import '../../../../widget/progress.dart';
import 'code_page.dart'; import 'code_page.dart';
import 'util.dart'; import '../../../../pages/util.dart';
class SigninPage extends StatefulWidget { class SigninPage extends StatefulWidget {
@override @override

View File

@@ -0,0 +1,37 @@
import 'package:fcs/fcs/common/data/providers/auth_fb.dart';
import 'package:fcs/fcs/common/data/providers/user_fb_data_provider.dart';
import 'package:fcs/fcs/common/data/providers/user_local_data_provider.dart';
import 'package:fcs/fcs/common/domain/entities/auth.dart';
import 'package:fcs/fcs/common/domain/entities/connectivity.dart';
import 'package:flutter/material.dart';
import 'auth_interface.dart';
class AuthImp implements AuthInterface {
AuthImp({
@required this.authFb,
@required this.connectivity,
@required this.userFBDataProvider,
@required this.userLocalDataProvider,
});
final Connectivity connectivity;
final UserFBDataProvider userFBDataProvider;
final UserLocalDataProvider userLocalDataProvider;
final AuthFb authFb;
@override
Future<Auth> sendSmsCodeToPhoneNumber(String phoneNumber) {
return authFb.sendSmsCodeToPhoneNumber(phoneNumber);
}
@override
Future<Auth> signInWithSmsCode(String smsCode) {
return authFb.signInWithPhoneNumber(smsCode);
}
@override
Future<Auth> logout() {
return authFb.logout();
}
}

View File

@@ -0,0 +1,7 @@
import 'package:fcs/fcs/common/domain/entities/auth.dart';
abstract class AuthInterface {
Future<Auth> sendSmsCodeToPhoneNumber(String phoneNumber);
Future<Auth> signInWithSmsCode(String smsCode);
Future<Auth> logout();
}

View File

@@ -0,0 +1,33 @@
import 'package:fcs/fcs/common/domain/entities/connectivity.dart';
import 'package:fcs/fcs/common/domain/entities/user.dart';
import 'package:fcs/fcs/common/domain/exceiptions/server_exceptions.dart';
import 'package:flutter/material.dart';
import 'user_interface.dart';
class UserImp implements UserInterface {
UserImp({
@required this.connectivity,
});
final Connectivity connectivity;
@override
Future<User> getUser(String id) async {
if (connectivity.isConnected) {
try {
final User user = User();
// await userFBDataProvider.getUser(id);
// cache product
// productLocalDataProvider.cacheProduct(product);
return user;
} catch (e) {
print(e);
return ServerException()();
}
} else {
return Future.value(User());
// return userLocalDataProvider.getUser(id);
}
}
}

View File

@@ -0,0 +1,5 @@
import 'package:fcs/fcs/common/domain/entities/user.dart';
abstract class UserInterface {
Future<User> getUser(String id);
}

17
lib/main-local.dart Normal file
View File

@@ -0,0 +1,17 @@
import 'package:flutter/material.dart';
import 'package:logging/logging.dart';
import 'package:fcs/config.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'app.dart';
void main() {
Config(
flavor: Flavor.DEV,
color: Colors.blue,
apiURL: "https://localhost:7777",
reportURL: "http://petrok-dev.mokkon.com:8080",
reportProjectID: "dev",
level: Level.ALL);
runApp(App());
}

View File

@@ -58,7 +58,7 @@ import 'my_registeration.dart';
import 'pd/pd_list.dart'; import 'pd/pd_list.dart';
import 'products_list.dart'; import 'products_list.dart';
import 'profile_page.dart'; import 'profile_page.dart';
import 'signin_page.dart'; import '../fcs/common/pages/signin/signin_page.dart';
import 'staff_list.dart'; import 'staff_list.dart';
import 'fcs_profile_page.dart'; import 'fcs_profile_page.dart';

View File

@@ -15,7 +15,7 @@ import 'package:provider/provider.dart';
import '../theme/theme.dart'; import '../theme/theme.dart';
import 'profile_page.dart'; import 'profile_page.dart';
import 'signin_page.dart'; import '../fcs/common/pages/signin/signin_page.dart';
import 'term.dart'; import 'term.dart';
final msgLog = Logger('backgroundMessageHandler'); final msgLog = Logger('backgroundMessageHandler');

View File

@@ -1,5 +1,5 @@
name: fcs name: fcs
description: A new Flutter project. description: FCS Logistics
publish_to: 'none' # Remove this line if you wish to publish to pub.dev publish_to: 'none' # Remove this line if you wish to publish to pub.dev
version: 1.0.0+1 version: 1.0.0+1