Merge branch 'master' of tzw/fcs into master
This commit is contained in:
@@ -19,6 +19,7 @@
|
||||
"btn.clear":"Clear Filter",
|
||||
"btn.filter":"Filter",
|
||||
"btn.exit_confirm":"Are you sure you want to exit?",
|
||||
"btn.confirm":"Confirm",
|
||||
"Buttons End ================================================================":"",
|
||||
|
||||
"Offline Start ================================================================":"",
|
||||
@@ -76,6 +77,8 @@
|
||||
"Login Start ================================================================":"",
|
||||
"login.title":"Sign in to FCS",
|
||||
"login.phone":"Enter phone number",
|
||||
"login.phone_number":"Phone Number",
|
||||
"login.phone_empty":"Enter phone number",
|
||||
"Login End ================================================================":"",
|
||||
|
||||
"SMS Start ================================================================":"",
|
||||
@@ -83,6 +86,7 @@
|
||||
"sms.six.digit":"Enter 6 digit sms code sent to",
|
||||
"sms.resend":"Resend",
|
||||
"sms.resend.seconds":"Resend again in {0} seconds",
|
||||
"sms.code":"SMS Code",
|
||||
"SMS End ================================================================":"",
|
||||
|
||||
"FAQ Start ================================================================":"",
|
||||
@@ -132,6 +136,7 @@
|
||||
"customer.list.title":"Customers",
|
||||
"customer.name":"Name",
|
||||
"customer.phone":"Phone number",
|
||||
"customer.phone_empty":"Please insert phone number",
|
||||
"customer.status":"Status",
|
||||
"customer.fcs.id":"FCS ID",
|
||||
"customer.invitation.request.confirm":"Accept customer",
|
||||
@@ -183,6 +188,21 @@
|
||||
"profile.email":"Email",
|
||||
"profile.privileges":"Privileges",
|
||||
"profile.default.delivery.address":"Default delivery address",
|
||||
"profile.recovery.email":"Recovery Email",
|
||||
"profile.change_phone.title":"Change Phone Number",
|
||||
"profile.current_phone":"Current Phone Number",
|
||||
"profile.new_phone":"New Phone Number",
|
||||
"profile.new_phone_empty":"Enter new phone number",
|
||||
"profile.send_sms":"Send SMS Code",
|
||||
"profile.confirm_new_phone.title":"Confirm New Phone Number",
|
||||
"profile.change_phone.btn":"Change",
|
||||
"profile.recovery_email.title":"Recovery Email",
|
||||
"profile.email.empty":"Enter recovery email",
|
||||
"profile.email.mismatch_message":"Email format is incorrect",
|
||||
"profile.email_instruction":"Your recovery email is used to help you get back into your account when you can’t sign in.",
|
||||
"profile.comfirm_email.title":"Confirm Your Email",
|
||||
"profile.confirm_email.send_to":"Sent email to",
|
||||
"profile.confirm_email.insruction":"Please open the email to confirm recovery email",
|
||||
"Profile End ================================================================":"",
|
||||
|
||||
"Package Start ================================================================":"",
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
"btn.clear":"Clear Filter",
|
||||
"btn.filter":"Filter",
|
||||
"btn.exit_confirm":"Are you sure you want to exit?",
|
||||
"btn.confirm":"အတည်ပြုမည်",
|
||||
"Buttons End ================================================================":"",
|
||||
|
||||
"Offline Start ================================================================":"",
|
||||
@@ -75,6 +76,8 @@
|
||||
"Login Start ================================================================":"",
|
||||
"login.title":"FCS သို့အကောင့်ဒ်၀င်ပါ",
|
||||
"login.phone":"ဖုန်းနံပါတ်ထည့်ပါ",
|
||||
"login.phone_number":"ဖုန်းနံပါတ်",
|
||||
"login.phone_empty":"ဖုန်းနံပါတ်ထည့်ပါ",
|
||||
"Login End ================================================================":"",
|
||||
|
||||
"SMS Start ================================================================":"",
|
||||
@@ -82,6 +85,7 @@
|
||||
"sms.six.digit":"SMS ဂဏန်း ခြောက်လုံး ကိုရိုက်ထဲ့ပါ",
|
||||
"sms.resend":"ပြန်ပို့ရန်",
|
||||
"sms.resend.seconds":"SMS ပြန်ပို့ရန် {0} စက္ကန့် စောင့်ပါ",
|
||||
"sms.code":"SMS Code",
|
||||
"SMS End ================================================================":"",
|
||||
|
||||
"FAQ Start ================================================================":"",
|
||||
@@ -132,6 +136,7 @@
|
||||
"customer.list.title":"ဝယ်ယူသူများ",
|
||||
"customer.name":"နာမည်",
|
||||
"customer.phone":"ဖုန်းနံပါတ်",
|
||||
"customer.phone_empty":"Please insert phone number",
|
||||
"customer.status":"အခြေအနေ",
|
||||
"customer.fcs.id":"FCS ID",
|
||||
"customer.invitation.request.confirm":"လက်ခံ လိုက်ပါ",
|
||||
@@ -184,6 +189,21 @@
|
||||
"profile.email":"အီးမေးလ်",
|
||||
"profile.privileges":"လုပ်ပိုင်ခွင့်များ",
|
||||
"profile.default.delivery.address":"အမြဲတမ်း ပို့ဆောင်ရမည့်လိပ်စာ",
|
||||
"profile.recovery.email":"အီးမေးလ်",
|
||||
"profile.change_phone.title":"ဖုန်းနံပါတ်ပြောင်းခြင်း",
|
||||
"profile.current_phone":"လက်ရှိဖုန်းနံပါတ်",
|
||||
"profile.new_phone":"ဖုန်းနံပါတ်အသစ်",
|
||||
"profile.new_phone_empty":"ဖုန်းနံပါတ်အသစ်ထည့်ပါ",
|
||||
"profile.send_sms":"SMS ကုဒ်ပို့မည်",
|
||||
"profile.confirm_new_phone.title":"ဖုန်းနံပါတ်အသစ်ကို အတည်ပြုခြင်း",
|
||||
"profile.change_phone.btn":"ပြောင်းမည်",
|
||||
"profile.recovery_email.title":"အီးမေးလ်ထည့်ခြင်း",
|
||||
"profile.email.empty":"အီးမေးလ်ထည့်ပါ",
|
||||
"profile.email.mismatch_message":"အီးမေးလ်မမှန်ပါ",
|
||||
"profile.email_instruction":"အကောင့်ဝင်လို့မရသောအခါတွင် သင့်အကောင့်ထဲသို့ ပြန်လည်ရောက်ရှိစေရန်အတွက် သင်၏အီးမေးလ်ကို အသုံးပြုပါသည်။",
|
||||
"profile.comfirm_email.title":"သင့်အီးမေးလ်ကိုအတည်ပြုခြင်း",
|
||||
"profile.confirm_email.send_to":"သင့်အီးမေးလ်သို့ ပေးပို့ခဲ့သည်",
|
||||
"profile.confirm_email.insruction":"အီးမေးလ်ကို အတည်ပြုရန် သင့်အီးမေးလ်ကိုဖွင့်ပါ",
|
||||
"Profile End ================================================================":"",
|
||||
|
||||
"Package Start ================================================================":"",
|
||||
|
||||
@@ -29,6 +29,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import 'helpers/theme.dart';
|
||||
import 'pages/carton/model/carton_selection_model.dart';
|
||||
import 'pages/carton/model/consignee_selection_model.dart';
|
||||
import 'pages/carton/model/package_selection_model.dart';
|
||||
@@ -165,6 +166,8 @@ class _AppState extends State<App> {
|
||||
debugShowCheckedModeBanner: false,
|
||||
theme: ThemeData(
|
||||
// useMaterial3: false,
|
||||
textSelectionTheme:
|
||||
const TextSelectionThemeData(cursorColor: primaryColor),
|
||||
dividerTheme: DividerThemeData(color: Colors.grey.shade200),
|
||||
colorScheme: ColorScheme.light(primary: Colors.white),
|
||||
dialogTheme: DialogTheme(
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
const uploadPhotoLimit = 10;
|
||||
const shipmentCountForCartonFilter = 10;
|
||||
const resendCountSec = 30;
|
||||
|
||||
const config_collection = "configs";
|
||||
const user_collection = "users";
|
||||
|
||||
@@ -29,7 +29,8 @@ class AuthFb {
|
||||
StreamSubscription<DocumentSnapshot>? userListener;
|
||||
StreamSubscription<DocumentSnapshot>? userAuthListener;
|
||||
|
||||
Future<fcs.AuthResult> sendSmsCodeToPhoneNumber(String phoneNumber) {
|
||||
Future<fcs.AuthResult> sendSmsCodeToPhoneNumber(String phoneNumber,
|
||||
{String? forceResendingToken}) {
|
||||
Completer<fcs.AuthResult> completer = Completer();
|
||||
bool codeSentCompleted = false;
|
||||
|
||||
@@ -66,7 +67,9 @@ class AuthFb {
|
||||
print("codeSent " + phoneNumber);
|
||||
codeSentCompleted = true;
|
||||
if (!completer.isCompleted)
|
||||
completer.complete(fcs.AuthResult(authStatus: AuthStatus.SMS_SENT));
|
||||
completer.complete(fcs.AuthResult(
|
||||
authStatus: AuthStatus.SMS_SENT,
|
||||
forceResendingToken: forceResendingToken?.toString()));
|
||||
};
|
||||
|
||||
final fb.PhoneCodeAutoRetrievalTimeout codeAutoRetrievalTimeout =
|
||||
@@ -83,6 +86,9 @@ class AuthFb {
|
||||
};
|
||||
|
||||
_fb.verifyPhoneNumber(
|
||||
forceResendingToken: forceResendingToken != null
|
||||
? int.tryParse(forceResendingToken)
|
||||
: null,
|
||||
phoneNumber: phoneNumber,
|
||||
timeout: const Duration(seconds: 0),
|
||||
verificationCompleted: verificationCompleted,
|
||||
|
||||
@@ -16,8 +16,10 @@ class AuthServiceImp implements AuthService {
|
||||
final AuthFb authFb;
|
||||
|
||||
@override
|
||||
Future<AuthResult> sendSmsCodeToPhoneNumber(String phoneNumber) {
|
||||
return authFb.sendSmsCodeToPhoneNumber(phoneNumber);
|
||||
Future<AuthResult> sendSmsCodeToPhoneNumber(String phoneNumber,
|
||||
{String? forceResendingToken}) {
|
||||
return authFb.sendSmsCodeToPhoneNumber(phoneNumber,
|
||||
forceResendingToken: forceResendingToken);
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -3,7 +3,8 @@ import 'package:fcs/domain/entities/setting.dart';
|
||||
import 'package:fcs/domain/entities/user.dart';
|
||||
|
||||
abstract class AuthService {
|
||||
Future<AuthResult> sendSmsCodeToPhoneNumber(String phoneNumber);
|
||||
Future<AuthResult> sendSmsCodeToPhoneNumber(String phoneNumber,
|
||||
{String? forceResendingToken});
|
||||
Future<AuthResult> signInWithSmsCode(String smsCode);
|
||||
Future<void> signoutStart();
|
||||
Future<void> signoutEnd();
|
||||
|
||||
@@ -4,6 +4,11 @@ class AuthResult {
|
||||
AuthStatus? authStatus;
|
||||
String? authErrorCode;
|
||||
String? authErrorMsg;
|
||||
String? forceResendingToken;
|
||||
|
||||
AuthResult({this.authStatus, this.authErrorCode, this.authErrorMsg});
|
||||
AuthResult(
|
||||
{this.authStatus,
|
||||
this.authErrorCode,
|
||||
this.authErrorMsg,
|
||||
this.forceResendingToken});
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ class User {
|
||||
bool enablePinLogin;
|
||||
String? pinDigit;
|
||||
List<String> privileges = [];
|
||||
String? recoveryEmail;
|
||||
|
||||
String get initial =>
|
||||
name != null && name != "" ? name!.substring(0, 1) : "?";
|
||||
@@ -51,7 +52,7 @@ class User {
|
||||
|
||||
// for pin login
|
||||
String? pinToken;
|
||||
bool get isPinLogin => pinToken!=null;
|
||||
bool get isPinLogin => pinToken != null;
|
||||
|
||||
String get phone => phoneNumber != null && phoneNumber!.startsWith("959")
|
||||
? "0${phoneNumber!.substring(2)}"
|
||||
@@ -62,21 +63,21 @@ class User {
|
||||
bool get disabled => status != null && status == user_disabled_status;
|
||||
|
||||
String get share => "Your phone number:$phoneNumber";
|
||||
User({
|
||||
this.id,
|
||||
this.name,
|
||||
this.phoneNumber,
|
||||
this.fcsID,
|
||||
this.status,
|
||||
this.privileges = const [],
|
||||
this.lastMessage,
|
||||
this.lastMessageTime,
|
||||
this.userUnseenCount = 0,
|
||||
this.fcsUnseenCount = 0,
|
||||
this.preferCurrency,
|
||||
this.enablePinLogin = false,
|
||||
this.pinDigit,
|
||||
});
|
||||
User(
|
||||
{this.id,
|
||||
this.name,
|
||||
this.phoneNumber,
|
||||
this.fcsID,
|
||||
this.status,
|
||||
this.privileges = const [],
|
||||
this.lastMessage,
|
||||
this.lastMessageTime,
|
||||
this.userUnseenCount = 0,
|
||||
this.fcsUnseenCount = 0,
|
||||
this.preferCurrency,
|
||||
this.enablePinLogin = false,
|
||||
this.pinDigit,
|
||||
this.recoveryEmail});
|
||||
|
||||
factory User.fromJson(Map<String, dynamic> json) {
|
||||
return User(
|
||||
|
||||
@@ -9,6 +9,7 @@ const buttonBkColor = const Color(0xFF268944);
|
||||
const labelColor = const Color(0xFF757575);
|
||||
var dividerColor = Colors.grey.shade400;
|
||||
const dangerColor = const Color(0xffff0606);
|
||||
const hintTextColor = Color.fromARGB(255, 187, 187, 187);
|
||||
|
||||
const TextStyle labelStyle =
|
||||
TextStyle(fontSize: 20, color: labelColor, fontWeight: FontWeight.w500);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import 'package:country_code_picker/country_code_picker.dart';
|
||||
import 'package:country_picker/country_picker.dart';
|
||||
import 'package:fcs/helpers/theme.dart';
|
||||
import 'package:fcs/pages/customer/model/customer_model.dart';
|
||||
import 'package:fcs/pages/main/util.dart';
|
||||
@@ -8,6 +8,8 @@ import 'package:fcs/pages/widgets/progress.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import '../widgets/input_phone.dart';
|
||||
|
||||
class InvitationCreate extends StatefulWidget {
|
||||
@override
|
||||
_InvitationCreateState createState() => _InvitationCreateState();
|
||||
@@ -18,13 +20,23 @@ class _InvitationCreateState extends State<InvitationCreate> {
|
||||
TextEditingController _phoneController = new TextEditingController();
|
||||
|
||||
bool _isLoading = false;
|
||||
late String dialCode;
|
||||
final _inviteFormKey = GlobalKey<FormState>();
|
||||
late Country _selectedCountry;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
dialCode = "+95";
|
||||
_selectedCountry = Country(
|
||||
name: 'Myanmar',
|
||||
countryCode: 'MM',
|
||||
displayName: 'Myanmar(MM)',
|
||||
displayNameNoCountryCode: 'Myanmar',
|
||||
e164Key: '',
|
||||
e164Sc: -1,
|
||||
example: '',
|
||||
geographic: false,
|
||||
level: -1,
|
||||
phoneCode: '95');
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -47,6 +59,18 @@ class _InvitationCreateState extends State<InvitationCreate> {
|
||||
},
|
||||
);
|
||||
|
||||
final phoneBox = InputPhone(
|
||||
lableKey: "customer.phone",
|
||||
validationLabel: "customer.phone_empty",
|
||||
phoneCtl: _phoneController,
|
||||
selectedCountry: _selectedCountry,
|
||||
onValueChange: (country) {
|
||||
setState(() {
|
||||
_selectedCountry = country;
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
return LocalProgress(
|
||||
inAsyncCall: _isLoading,
|
||||
child: Scaffold(
|
||||
@@ -73,76 +97,8 @@ class _InvitationCreateState extends State<InvitationCreate> {
|
||||
children: <Widget>[
|
||||
nameBox,
|
||||
SizedBox(height: 10),
|
||||
Row(
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Icon(
|
||||
Icons.phone,
|
||||
color: primaryColor,
|
||||
),
|
||||
),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
border:
|
||||
Border.all(color: Colors.grey.shade400, width: 1),
|
||||
borderRadius:
|
||||
BorderRadius.all(Radius.circular(12.0))),
|
||||
child: CountryCodePicker(
|
||||
onChanged: _countryChange,
|
||||
initialSelection: dialCode,
|
||||
countryFilter: ['mm', 'us'],
|
||||
showCountryOnly: false,
|
||||
showOnlyCountryWhenClosed: false,
|
||||
alignLeft: false,
|
||||
textStyle:
|
||||
TextStyle(fontSize: 16, color: Colors.black87),
|
||||
searchDecoration: InputDecoration(
|
||||
focusedBorder: UnderlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Colors.black, width: 1.0))),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: 10,
|
||||
),
|
||||
Flexible(
|
||||
child: Container(
|
||||
padding: EdgeInsets.only(top: 10, bottom: 10),
|
||||
child: TextFormField(
|
||||
controller: _phoneController,
|
||||
cursorColor: primaryColor,
|
||||
textAlign: TextAlign.left,
|
||||
keyboardType: TextInputType.phone,
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
),
|
||||
decoration: InputDecoration(
|
||||
fillColor: Colors.white,
|
||||
labelText:
|
||||
getLocalString(context, "customer.phone"),
|
||||
labelStyle:
|
||||
TextStyle(fontSize: 16, color: Colors.grey),
|
||||
filled: true,
|
||||
focusedBorder: UnderlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Colors.grey, width: 1.0)),
|
||||
errorStyle: TextStyle(
|
||||
color: dangerColor,
|
||||
)),
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return "Please insert phone no";
|
||||
}
|
||||
return null;
|
||||
},
|
||||
autovalidateMode: AutovalidateMode.onUserInteraction,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 20),
|
||||
phoneBox,
|
||||
SizedBox(height: 30),
|
||||
fcsButton(context, getLocalString(context, "invite.btn"),
|
||||
callack: _invite),
|
||||
],
|
||||
@@ -153,23 +109,21 @@ class _InvitationCreateState extends State<InvitationCreate> {
|
||||
);
|
||||
}
|
||||
|
||||
_countryChange(CountryCode countryCode) {
|
||||
setState(() {
|
||||
dialCode = countryCode.dialCode!;
|
||||
});
|
||||
}
|
||||
|
||||
_invite() async {
|
||||
String userName = _nameController.text;
|
||||
String phoneNumber = dialCode + _phoneController.text;
|
||||
|
||||
if (!_inviteFormKey.currentState!.validate()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
setState(() {
|
||||
_isLoading = true;
|
||||
});
|
||||
try {
|
||||
String userName = _nameController.text;
|
||||
|
||||
bool isMyanmar = _selectedCountry.phoneCode == "95";
|
||||
String dialCode = isMyanmar ? "+959" : "+${_selectedCountry.phoneCode}";
|
||||
String phoneNumber = dialCode + _phoneController.text;
|
||||
|
||||
CustomerModel customerModel =
|
||||
Provider.of<CustomerModel>(context, listen: false);
|
||||
await customerModel.inviteUser(userName, phoneNumber);
|
||||
|
||||
@@ -153,8 +153,10 @@ class MainModel extends ChangeNotifier {
|
||||
return int.parse(packageInfo!.buildNumber) >= setting!.supportBuildNum;
|
||||
}
|
||||
|
||||
Future<AuthResult> sendSms(String phoneNumber) {
|
||||
return Services.instance.authService.sendSmsCodeToPhoneNumber(phoneNumber);
|
||||
Future<AuthResult> sendSms(String phoneNumber,
|
||||
{String? forceResendingToken}) {
|
||||
return Services.instance.authService.sendSmsCodeToPhoneNumber(phoneNumber,
|
||||
forceResendingToken: forceResendingToken);
|
||||
}
|
||||
|
||||
Future<AuthResult> signin(String smsCode) async {
|
||||
|
||||
@@ -403,3 +403,12 @@ String removeTrailingZeros(double number) {
|
||||
: result;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
bool isValidEmail(String email) {
|
||||
// Define a regular expression for validating an email
|
||||
final emailRegex = RegExp(r'^[^@\s]+@[^@\s]+\.[^@\s]+$');
|
||||
|
||||
// Check if the email matches the pattern
|
||||
return emailRegex.hasMatch(email);
|
||||
}
|
||||
114
lib/pages/profile/add_recovery_email.dart
Normal file
114
lib/pages/profile/add_recovery_email.dart
Normal file
@@ -0,0 +1,114 @@
|
||||
// ignore_for_file: use_build_context_synchronously
|
||||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../domain/entities/user.dart';
|
||||
import '../../helpers/theme.dart';
|
||||
import '../../localization/app_translations.dart';
|
||||
import '../main/util.dart';
|
||||
import '../widgets/input_text.dart';
|
||||
import '../widgets/local_app_bar.dart';
|
||||
import '../widgets/local_text.dart';
|
||||
import '../widgets/progress.dart';
|
||||
import 'confirm_recovery_email.dart';
|
||||
|
||||
class AddRecoveryEmail extends StatefulWidget {
|
||||
final User user;
|
||||
const AddRecoveryEmail({super.key, required this.user});
|
||||
|
||||
@override
|
||||
State<AddRecoveryEmail> createState() => _AddRecoveryEmailState();
|
||||
}
|
||||
|
||||
class _AddRecoveryEmailState extends State<AddRecoveryEmail> {
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
bool _isLoading = false;
|
||||
final TextEditingController _emailCtl = TextEditingController();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_emailCtl.text = widget.user.recoveryEmail ?? "";
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final emailBox = InputText(
|
||||
labelTextKey: "profile.email",
|
||||
iconData: Icons.email_outlined,
|
||||
controller: _emailCtl,
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return AppTranslations.of(context)!.text('profile.email.empty');
|
||||
}
|
||||
|
||||
if (!isValidEmail(value)) {
|
||||
return AppTranslations.of(context)!
|
||||
.text('profile.email.mismatch_message');
|
||||
}
|
||||
return null;
|
||||
},
|
||||
);
|
||||
|
||||
final continueBtn = Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 30),
|
||||
child: fcsButton(
|
||||
context,
|
||||
getLocalString(context, 'btn.continue'),
|
||||
callack: _continue,
|
||||
),
|
||||
);
|
||||
|
||||
return LocalProgress(
|
||||
inAsyncCall: _isLoading,
|
||||
child: Scaffold(
|
||||
appBar: const LocalAppBar(
|
||||
labelKey: 'profile.recovery_email.title',
|
||||
backgroundColor: Colors.white,
|
||||
labelColor: primaryColor,
|
||||
arrowColor: primaryColor),
|
||||
body: Form(
|
||||
key: _formKey,
|
||||
child: ListView(
|
||||
padding: const EdgeInsets.only(left: 15, right: 15, top: 10),
|
||||
children: [
|
||||
LocalText(context, 'profile.email_instruction',
|
||||
fontSize: 16, color: Colors.black),
|
||||
const SizedBox(height: 15),
|
||||
emailBox,
|
||||
const SizedBox(height: 30),
|
||||
continueBtn
|
||||
],
|
||||
)),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _continue() async {
|
||||
if (_formKey.currentState != null && !_formKey.currentState!.validate()) {
|
||||
return;
|
||||
}
|
||||
|
||||
setState(() {
|
||||
_isLoading = true;
|
||||
});
|
||||
|
||||
try {
|
||||
bool? updated = await Navigator.of(context, rootNavigator: true).push(
|
||||
CupertinoPageRoute(
|
||||
builder: (context) =>
|
||||
ConfirmRecoveryEmail(email: _emailCtl.text)));
|
||||
|
||||
if (updated ?? false) {
|
||||
Navigator.pop(context, true);
|
||||
}
|
||||
} catch (e) {
|
||||
showMsgDialog(context, "Error", e.toString());
|
||||
} finally {
|
||||
setState(() {
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
142
lib/pages/profile/change_phone_number.dart
Normal file
142
lib/pages/profile/change_phone_number.dart
Normal file
@@ -0,0 +1,142 @@
|
||||
// ignore_for_file: use_build_context_synchronously
|
||||
import 'package:country_picker/country_picker.dart';
|
||||
import 'package:fcs/helpers/theme.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import '../../domain/entities/auth_result.dart';
|
||||
import '../../domain/entities/auth_status.dart';
|
||||
import '../../domain/entities/user.dart';
|
||||
import '../main/model/main_model.dart';
|
||||
import '../main/util.dart';
|
||||
import '../widgets/display_text.dart';
|
||||
import '../widgets/input_phone.dart';
|
||||
import '../widgets/local_app_bar.dart';
|
||||
import '../widgets/progress.dart';
|
||||
import 'confirm_phone_number.dart';
|
||||
|
||||
class ChangePhoneNumber extends StatefulWidget {
|
||||
final User user;
|
||||
const ChangePhoneNumber({super.key, required this.user});
|
||||
|
||||
@override
|
||||
State<ChangePhoneNumber> createState() => _ChangePhoneNumberState();
|
||||
}
|
||||
|
||||
class _ChangePhoneNumberState extends State<ChangePhoneNumber> {
|
||||
bool _isLoading = false;
|
||||
TextEditingController _newPhoneCtl = TextEditingController();
|
||||
late Country _selectedCountry;
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_selectedCountry = Country(
|
||||
name: 'Myanmar',
|
||||
countryCode: 'MM',
|
||||
displayName: 'Myanmar(MM)',
|
||||
displayNameNoCountryCode: 'Myanmar',
|
||||
e164Key: '',
|
||||
e164Sc: -1,
|
||||
example: '',
|
||||
geographic: false,
|
||||
level: -1,
|
||||
phoneCode: '95');
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final currentPhoneBox = DisplayText(
|
||||
text: widget.user.phone,
|
||||
labelTextKey: "profile.current_phone",
|
||||
iconData: Icons.phone);
|
||||
|
||||
final newPhoneBox = InputPhone(
|
||||
autofocus: true,
|
||||
lableKey: "profile.new_phone",
|
||||
validationLabel: "profile.new_phone_empty",
|
||||
phoneCtl: _newPhoneCtl,
|
||||
selectedCountry: _selectedCountry,
|
||||
onValueChange: (country) {
|
||||
setState(() {
|
||||
_selectedCountry = country;
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
final sendBtn = Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 30),
|
||||
child: fcsButton(
|
||||
context,
|
||||
getLocalString(context, 'profile.send_sms'),
|
||||
callack: _sendSMS,
|
||||
),
|
||||
);
|
||||
|
||||
return Form(
|
||||
key: _formKey,
|
||||
child: LocalProgress(
|
||||
inAsyncCall: _isLoading,
|
||||
child: Scaffold(
|
||||
appBar: const LocalAppBar(
|
||||
labelKey: 'profile.change_phone.title',
|
||||
backgroundColor: Colors.white,
|
||||
labelColor: primaryColor,
|
||||
arrowColor: primaryColor,
|
||||
),
|
||||
body: ListView(
|
||||
padding: const EdgeInsets.only(left: 15, right: 15, top: 10),
|
||||
children: [
|
||||
currentPhoneBox,
|
||||
const SizedBox(height: 15),
|
||||
newPhoneBox,
|
||||
const SizedBox(height: 30),
|
||||
sendBtn
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _sendSMS() async {
|
||||
if (_formKey.currentState != null && !_formKey.currentState!.validate()) {
|
||||
return;
|
||||
}
|
||||
if (_newPhoneCtl.text.length < 5) {
|
||||
showMsgDialog(context, "Error", "Invalid phone number");
|
||||
return;
|
||||
}
|
||||
|
||||
setState(() {
|
||||
_isLoading = true;
|
||||
});
|
||||
|
||||
try {
|
||||
bool isMyanmar = _selectedCountry.phoneCode == "95";
|
||||
String dialCode = isMyanmar ? "+959" : "+${_selectedCountry.phoneCode}";
|
||||
String phoneNumber = dialCode + _newPhoneCtl.text;
|
||||
|
||||
AuthResult auth = await context.read<MainModel>().sendSms(phoneNumber);
|
||||
if (auth.authStatus == AuthStatus.SMS_SENT) {
|
||||
bool? updated = await Navigator.of(context, rootNavigator: true).push(
|
||||
CupertinoPageRoute(
|
||||
builder: (context) => ConfirmPhoneNumber(
|
||||
phoneNumber: phoneNumber,
|
||||
forceResendingToken: auth.forceResendingToken)));
|
||||
if (updated ?? false) {
|
||||
Navigator.pop(context, true);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
showMsgDialog(context, "Error", e.toString());
|
||||
} finally {
|
||||
setState(() {
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
232
lib/pages/profile/confirm_phone_number.dart
Normal file
232
lib/pages/profile/confirm_phone_number.dart
Normal file
@@ -0,0 +1,232 @@
|
||||
// ignore_for_file: use_build_context_synchronously
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:fcs/constants.dart';
|
||||
import 'package:fcs/pages/widgets/local_app_bar.dart';
|
||||
import 'package:fcs/pages/widgets/progress.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:pin_input_text_field/pin_input_text_field.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import '../../domain/entities/auth_result.dart';
|
||||
import '../../domain/entities/auth_status.dart';
|
||||
import '../../helpers/theme.dart';
|
||||
import '../main/model/main_model.dart';
|
||||
import '../main/util.dart';
|
||||
import '../widgets/local_text.dart';
|
||||
|
||||
class ConfirmPhoneNumber extends StatefulWidget {
|
||||
final String phoneNumber;
|
||||
final String? forceResendingToken;
|
||||
const ConfirmPhoneNumber(
|
||||
{super.key, required this.phoneNumber, this.forceResendingToken});
|
||||
|
||||
@override
|
||||
State<ConfirmPhoneNumber> createState() => _ConfirmPhoneNumberState();
|
||||
}
|
||||
|
||||
class _ConfirmPhoneNumberState extends State<ConfirmPhoneNumber> {
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
bool _isLoading = false;
|
||||
late String pin;
|
||||
late bool allNumberEntered;
|
||||
late Timer _timer;
|
||||
int _start = resendCountSec;
|
||||
bool canResend = false;
|
||||
String? _forceResendingToken;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
pin = "";
|
||||
allNumberEntered = false;
|
||||
_forceResendingToken = widget.forceResendingToken;
|
||||
super.initState();
|
||||
startTimer();
|
||||
}
|
||||
|
||||
void startTimer() {
|
||||
_timer = Timer.periodic(
|
||||
const Duration(seconds: 1),
|
||||
(t) => setState(
|
||||
() {
|
||||
if (_start < 1) {
|
||||
t.cancel();
|
||||
canResend = true;
|
||||
} else {
|
||||
_start = _start - 1;
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_timer.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
bool isMyanmar = widget.phoneNumber.startsWith("+959");
|
||||
|
||||
return LocalProgress(
|
||||
inAsyncCall: _isLoading,
|
||||
child: Scaffold(
|
||||
appBar: const LocalAppBar(
|
||||
labelKey: 'profile.confirm_new_phone.title',
|
||||
backgroundColor: Colors.white,
|
||||
labelColor: primaryColor,
|
||||
arrowColor: primaryColor),
|
||||
body: Form(
|
||||
key: _formKey,
|
||||
child: ListView(
|
||||
padding: const EdgeInsets.only(left: 15, right: 15, top: 10),
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.only(top: 40),
|
||||
child: Row(
|
||||
children: [
|
||||
Flexible(
|
||||
child: LocalText(context, "sms.six.digit",
|
||||
fontSize: 16, color: labelColor),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Text(
|
||||
widget.phoneNumber.startsWith("+959")
|
||||
? "0${widget.phoneNumber.substring(3)}"
|
||||
: widget.phoneNumber,
|
||||
style: const TextStyle(color: primaryColor, fontSize: 16),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 30,
|
||||
),
|
||||
LocalText(context, "sms.code", color: labelColor, fontSize: 16),
|
||||
Container(
|
||||
padding: const EdgeInsets.only(top: 10),
|
||||
child: PinInputTextField(
|
||||
pinLength: 6,
|
||||
cursor: Cursor(
|
||||
color: primaryColor,
|
||||
enabled: true,
|
||||
width: 2,
|
||||
height: 23),
|
||||
decoration: BoxLooseDecoration(
|
||||
textStyle: TextStyle(
|
||||
color:
|
||||
Theme.of(context).textTheme.labelMedium!.color,
|
||||
fontSize: 20),
|
||||
strokeColorBuilder:
|
||||
const FixedColorBuilder(labelColor)),
|
||||
textInputAction: TextInputAction.done,
|
||||
autoFocus: true,
|
||||
onChanged: _pinChange,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 40),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 30),
|
||||
child: fcsButton(
|
||||
context,
|
||||
getLocalString(context, 'profile.change_phone.btn'),
|
||||
callack: allNumberEntered
|
||||
? () {
|
||||
_change();
|
||||
}
|
||||
: null,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 30),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: <Widget>[
|
||||
ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor:
|
||||
canResend ? Colors.white : Colors.grey,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8.0),
|
||||
side: BorderSide(
|
||||
color: canResend
|
||||
? primaryColor
|
||||
: Colors.grey.shade400))),
|
||||
onPressed: canResend
|
||||
? () {
|
||||
_resend(isMyanmar);
|
||||
}
|
||||
: null,
|
||||
child: LocalText(context, "sms.resend",
|
||||
color:
|
||||
canResend ? primaryColor : Colors.grey.shade400,
|
||||
fontSize: 16),
|
||||
),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
children: <Widget>[
|
||||
LocalText(context, 'sms.resend.seconds',
|
||||
fontSize: 15,
|
||||
translationVariables: [_start.toString()],
|
||||
color: primaryColor),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
],
|
||||
)),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
_pinChange(pin) {
|
||||
setState(() {
|
||||
this.pin = pin;
|
||||
allNumberEntered = this.pin.length == 6;
|
||||
});
|
||||
}
|
||||
|
||||
_resend(bool isMyanmar) async {
|
||||
try {
|
||||
setState(() {
|
||||
_start = resendCountSec;
|
||||
canResend = false;
|
||||
});
|
||||
|
||||
_timer.cancel();
|
||||
startTimer();
|
||||
|
||||
var mainModel = context.read<MainModel>();
|
||||
|
||||
AuthResult auth = await mainModel.sendSms(widget.phoneNumber,
|
||||
forceResendingToken: _forceResendingToken);
|
||||
|
||||
if (auth.authStatus == AuthStatus.SMS_SENT) {
|
||||
_forceResendingToken = auth.forceResendingToken;
|
||||
}
|
||||
} catch (e) {
|
||||
await showMsgDialog(context, "Error", e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
_change() async {
|
||||
setState(() {
|
||||
_isLoading = true;
|
||||
});
|
||||
|
||||
try {
|
||||
// var mainModel = context.read<MainModel>();
|
||||
// await mainModel.updatePhoneNumber(pin);
|
||||
Navigator.pop(context, true);
|
||||
} catch (e) {
|
||||
await showMsgDialog(context, "Error", e.toString());
|
||||
} finally {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
138
lib/pages/profile/confirm_recovery_email.dart
Normal file
138
lib/pages/profile/confirm_recovery_email.dart
Normal file
@@ -0,0 +1,138 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:pin_input_text_field/pin_input_text_field.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import '../../helpers/theme.dart';
|
||||
import '../../localization/app_translations.dart';
|
||||
import '../main/model/language_model.dart';
|
||||
import '../main/util.dart';
|
||||
import '../widgets/local_app_bar.dart';
|
||||
import '../widgets/local_text.dart';
|
||||
import '../widgets/progress.dart';
|
||||
|
||||
class ConfirmRecoveryEmail extends StatefulWidget {
|
||||
final String email;
|
||||
const ConfirmRecoveryEmail({super.key, required this.email});
|
||||
|
||||
@override
|
||||
State<ConfirmRecoveryEmail> createState() => _ConfirmRecoveryEmailState();
|
||||
}
|
||||
|
||||
class _ConfirmRecoveryEmailState extends State<ConfirmRecoveryEmail> {
|
||||
bool _isLoading = false;
|
||||
late String pin;
|
||||
late bool allNumberEntered;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
pin = "";
|
||||
allNumberEntered = false;
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var isEng = context.watch<LanguageModel>().isEng;
|
||||
|
||||
return LocalProgress(
|
||||
inAsyncCall: _isLoading,
|
||||
child: Scaffold(
|
||||
appBar: const LocalAppBar(
|
||||
labelKey: 'profile.comfirm_email.title',
|
||||
backgroundColor: Colors.white,
|
||||
labelColor: primaryColor,
|
||||
arrowColor: primaryColor),
|
||||
body: ListView(
|
||||
padding: const EdgeInsets.only(left: 15, right: 15, top: 20),
|
||||
children: [
|
||||
RichText(
|
||||
text: TextSpan(children: [
|
||||
TextSpan(
|
||||
text: AppTranslations.of(context)!
|
||||
.text("profile.confirm_email.send_to"),
|
||||
style: isEng
|
||||
? newLabelStyle(
|
||||
fontWeight: FontWeight.normal,
|
||||
color:
|
||||
labelColor,
|
||||
fontSize: 16)
|
||||
: newLabelStyleMM(
|
||||
fontWeight: FontWeight.normal,
|
||||
color:
|
||||
labelColor,
|
||||
fontSize: 16)),
|
||||
TextSpan(
|
||||
text: " ${widget.email}",
|
||||
style: const TextStyle(fontSize: 16, color: primaryColor))
|
||||
]),
|
||||
),
|
||||
const SizedBox(height: 40),
|
||||
LocalText(
|
||||
context,
|
||||
'profile.confirm_email.insruction',
|
||||
fontSize: 16,
|
||||
color: labelColor,
|
||||
fontWeight: FontWeight.normal,
|
||||
),
|
||||
const SizedBox(height: 40),
|
||||
LocalText(context, "sms.code",
|
||||
color: labelColor, fontSize: 16),
|
||||
Container(
|
||||
padding: const EdgeInsets.only(top: 10),
|
||||
child: PinInputTextField(
|
||||
pinLength: 6,
|
||||
cursor: Cursor(
|
||||
color: primaryColor, enabled: true, width: 2, height: 23),
|
||||
decoration: BoxLooseDecoration(
|
||||
textStyle: TextStyle(
|
||||
color: Theme.of(context).textTheme.labelMedium!.color,
|
||||
fontSize: 20),
|
||||
strokeColorBuilder: const FixedColorBuilder(labelColor)),
|
||||
textInputAction: TextInputAction.done,
|
||||
autoFocus: true,
|
||||
onChanged: _pinChange,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 40),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 30),
|
||||
child: fcsButton(
|
||||
context,
|
||||
getLocalString(context, 'btn.confirm'),
|
||||
callack: allNumberEntered
|
||||
? () {
|
||||
_confirm();
|
||||
}
|
||||
: null,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
_pinChange(pin) {
|
||||
setState(() {
|
||||
this.pin = pin;
|
||||
allNumberEntered = this.pin.length == 6;
|
||||
});
|
||||
}
|
||||
|
||||
_confirm() async {
|
||||
setState(() {
|
||||
_isLoading = true;
|
||||
});
|
||||
|
||||
try {
|
||||
Navigator.pop(context, true);
|
||||
} catch (e) {
|
||||
// ignore: use_build_context_synchronously
|
||||
await showMsgDialog(context, "Error", e.toString());
|
||||
} finally {
|
||||
setState(() {
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,8 @@ import '../../helpers/theme.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
|
||||
import 'account_delection_page.dart';
|
||||
import 'add_recovery_email.dart';
|
||||
import 'change_phone_number.dart';
|
||||
|
||||
typedef void ProfileCallback();
|
||||
|
||||
@@ -69,44 +71,70 @@ class _ProfileState extends State<Profile> {
|
||||
if (mainModel.user == null) {
|
||||
return Container();
|
||||
}
|
||||
User user = mainModel.user!;
|
||||
|
||||
buildLanguage(languageModel);
|
||||
DeliveryAddressModel deliveryAddressModel =
|
||||
Provider.of<DeliveryAddressModel>(context);
|
||||
var deliveryAddressModel = Provider.of<DeliveryAddressModel>(context);
|
||||
|
||||
final namebox = DisplayText(
|
||||
text: "${mainModel.user!.name ?? ''}" +
|
||||
" (${mainModel.user!.status ?? ''})",
|
||||
text: "${user.name ?? ''}" + " (${user.status ?? ''})",
|
||||
labelTextKey: "profile.name",
|
||||
iconData: Icons.person,
|
||||
);
|
||||
final currencyBox = DisplayText(
|
||||
text: mainModel.user!.preferCurrency ?? "",
|
||||
labelTextKey: "profile.currency",
|
||||
iconData: FontAwesome5Regular.money_bill_alt,
|
||||
|
||||
final currencyBox = Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: DisplayText(
|
||||
text: user.preferCurrency ?? "",
|
||||
labelTextKey: "profile.currency",
|
||||
iconData: FontAwesome5Regular.money_bill_alt,
|
||||
)),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(right: 0),
|
||||
child: IconButton(
|
||||
icon: Icon(Icons.edit, color: Colors.grey),
|
||||
onPressed: _editCurrency),
|
||||
)
|
||||
],
|
||||
);
|
||||
|
||||
final deleteAccountBox = DisplayText(
|
||||
labelTextKey: "profile.delete.title",
|
||||
iconData: MaterialCommunityIcons.account_remove,
|
||||
);
|
||||
|
||||
final phonenumberbox = DisplayText(
|
||||
text: mainModel.user!.phone,
|
||||
labelTextKey: "profile.phone",
|
||||
iconData: Icons.phone,
|
||||
final phonenumberBox = Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: DisplayText(
|
||||
text: user.phone,
|
||||
labelTextKey: "profile.phone",
|
||||
iconData: Icons.phone),
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(Icons.edit, color: Colors.grey),
|
||||
onPressed: () {
|
||||
Navigator.of(context, rootNavigator: true).push(
|
||||
CupertinoPageRoute(
|
||||
builder: (context) => ChangePhoneNumber(user: user)));
|
||||
})
|
||||
],
|
||||
);
|
||||
|
||||
final fcsIDBox = Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: DisplayText(
|
||||
text: mainModel.user!.fcsID ?? "",
|
||||
text: user.fcsID ?? "",
|
||||
labelTextKey: "customer.fcs.id",
|
||||
icon: FcsIDIcon(),
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(Icons.content_copy, color: Colors.grey),
|
||||
onPressed: () => _copy(getLocalString(context, "customer.fcs.id"),
|
||||
mainModel.user!.fcsID ?? ""),
|
||||
onPressed: () => _copy(
|
||||
getLocalString(context, "customer.fcs.id"), user.fcsID ?? ""),
|
||||
)
|
||||
],
|
||||
);
|
||||
@@ -141,6 +169,22 @@ class _ProfileState extends State<Profile> {
|
||||
},
|
||||
iconData: Icons.exit_to_app);
|
||||
|
||||
final emailBox = Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: DisplayText(
|
||||
text: user.recoveryEmail,
|
||||
labelTextKey: "profile.recovery.email",
|
||||
iconData: Icons.email_outlined,
|
||||
),
|
||||
),
|
||||
IconButton(icon: Icon(Icons.edit, color: Colors.grey), onPressed: () {
|
||||
Navigator.of(context, rootNavigator: true).push(
|
||||
CupertinoPageRoute(
|
||||
builder: (context) => AddRecoveryEmail(user: user)));
|
||||
})
|
||||
],
|
||||
);
|
||||
return LocalProgress(
|
||||
inAsyncCall: _isLoading,
|
||||
child: Scaffold(
|
||||
@@ -165,20 +209,11 @@ class _ProfileState extends State<Profile> {
|
||||
)
|
||||
],
|
||||
),
|
||||
phonenumberbox,
|
||||
phonenumberBox,
|
||||
fcsIDBox,
|
||||
usaShippingAddressBox,
|
||||
Row(
|
||||
children: <Widget>[
|
||||
Expanded(child: currencyBox),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(right: 0),
|
||||
child: IconButton(
|
||||
icon: Icon(Icons.edit, color: Colors.grey),
|
||||
onPressed: _editCurrency),
|
||||
)
|
||||
],
|
||||
),
|
||||
currencyBox,
|
||||
emailBox,
|
||||
DefaultDeliveryAddress(
|
||||
labelKey: "profile.default.delivery.address",
|
||||
deliveryAddress: deliveryAddressModel.defalutAddress,
|
||||
@@ -298,20 +333,22 @@ class _ProfileState extends State<Profile> {
|
||||
}
|
||||
});
|
||||
|
||||
return Column(
|
||||
children: <Widget>[
|
||||
DisplayText(
|
||||
labelTextKey: "profile.privileges",
|
||||
iconData: MaterialCommunityIcons.clipboard_check_outline,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 30.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: getRowPrivilegeWidget(privileges)),
|
||||
)
|
||||
],
|
||||
);
|
||||
return privileges.isEmpty
|
||||
? const SizedBox()
|
||||
: Column(
|
||||
children: <Widget>[
|
||||
DisplayText(
|
||||
labelTextKey: "profile.privileges",
|
||||
iconData: MaterialCommunityIcons.clipboard_check_outline,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 30.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: getRowPrivilegeWidget(privileges)),
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
List<Widget> getRowPrivilegeWidget(List<Privilege> privileges) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import 'package:country_code_picker/country_code_picker.dart';
|
||||
import 'package:country_picker/country_picker.dart';
|
||||
import 'package:fcs/domain/entities/auth_result.dart';
|
||||
import 'package:fcs/domain/entities/auth_status.dart';
|
||||
import 'package:fcs/pages/main/model/main_model.dart';
|
||||
@@ -13,6 +13,7 @@ import 'package:provider/provider.dart';
|
||||
|
||||
import '../../helpers/theme.dart';
|
||||
import '../main/util.dart';
|
||||
import '../widgets/input_phone.dart';
|
||||
import 'sms_code_page.dart';
|
||||
|
||||
class SigninPage extends StatefulWidget {
|
||||
@@ -25,15 +26,22 @@ class _SigninPageState extends State<SigninPage> {
|
||||
late String dialCode;
|
||||
|
||||
TextEditingController phonenumberCtl = new TextEditingController();
|
||||
late Country _selectedCountry;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
phonenumberCtl = new TextEditingController(text: "09");
|
||||
phonenumberCtl.selection = TextSelection.fromPosition(
|
||||
TextPosition(offset: phonenumberCtl.text.length));
|
||||
|
||||
dialCode = "+95";
|
||||
_selectedCountry = Country(
|
||||
name: 'Myanmar',
|
||||
countryCode: 'MM',
|
||||
displayName: 'Myanmar(MM)',
|
||||
displayNameNoCountryCode: 'Myanmar',
|
||||
e164Key: '',
|
||||
e164Sc: -1,
|
||||
example: '',
|
||||
geographic: false,
|
||||
level: -1,
|
||||
phoneCode: '95');
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -67,53 +75,16 @@ class _SigninPageState extends State<SigninPage> {
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
Row(
|
||||
children: <Widget>[
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: Colors.grey.shade400, width: 1),
|
||||
borderRadius: BorderRadius.all(Radius.circular(12.0))),
|
||||
child: CountryCodePicker(
|
||||
onChanged: _countryChange,
|
||||
initialSelection: dialCode,
|
||||
countryFilter: ['mm', 'us'],
|
||||
showCountryOnly: false,
|
||||
showOnlyCountryWhenClosed: false,
|
||||
alignLeft: false,
|
||||
textStyle: TextStyle(fontSize: 16, color: Colors.black87),
|
||||
searchDecoration: InputDecoration(
|
||||
focusedBorder: UnderlineInputBorder(
|
||||
borderSide:
|
||||
BorderSide(color: Colors.black, width: 1.0)))),
|
||||
),
|
||||
SizedBox(
|
||||
width: 10,
|
||||
),
|
||||
Flexible(
|
||||
child: Container(
|
||||
padding: EdgeInsets.only(top: 10, bottom: 10),
|
||||
child: TextFormField(
|
||||
controller: phonenumberCtl,
|
||||
cursorColor: primaryColor,
|
||||
textAlign: TextAlign.left,
|
||||
autofocus: true,
|
||||
keyboardType: TextInputType.phone,
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
),
|
||||
decoration: new InputDecoration(
|
||||
contentPadding: EdgeInsets.only(top: 8),
|
||||
enabledBorder: UnderlineInputBorder(
|
||||
borderSide:
|
||||
BorderSide(color: primaryColor, width: 1.0)),
|
||||
focusedBorder: UnderlineInputBorder(
|
||||
borderSide:
|
||||
BorderSide(color: primaryColor, width: 1.0)),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
InputPhone(
|
||||
autofocus: true,
|
||||
validationLabel: "login.phone_empty",
|
||||
phoneCtl: phonenumberCtl,
|
||||
selectedCountry: _selectedCountry,
|
||||
onValueChange: (country) {
|
||||
setState(() {
|
||||
_selectedCountry = country;
|
||||
});
|
||||
},
|
||||
),
|
||||
SizedBox(
|
||||
height: 20,
|
||||
@@ -140,15 +111,8 @@ class _SigninPageState extends State<SigninPage> {
|
||||
);
|
||||
}
|
||||
|
||||
_countryChange(CountryCode countryCode) {
|
||||
setState(() {
|
||||
dialCode = countryCode.dialCode ?? '+95';
|
||||
});
|
||||
}
|
||||
|
||||
_next() async {
|
||||
String phoneNumber = phonenumberCtl.text;
|
||||
if (phoneNumber.length < 5) {
|
||||
if (phonenumberCtl.text.length < 5) {
|
||||
showMsgDialog(context, "Error", "Invalid phone number");
|
||||
return;
|
||||
}
|
||||
@@ -157,16 +121,17 @@ class _SigninPageState extends State<SigninPage> {
|
||||
_isLoading = true;
|
||||
});
|
||||
|
||||
try {
|
||||
phoneNumber = phoneNumber[0] == "0"
|
||||
? phoneNumber.replaceFirst("0", "")
|
||||
: phoneNumber;
|
||||
phoneNumber = dialCode + phoneNumber;
|
||||
bool isMyanmar = _selectedCountry.phoneCode == "95";
|
||||
String dialCode = isMyanmar ? "+959" : "+${_selectedCountry.phoneCode}";
|
||||
|
||||
try {
|
||||
String phoneNumber = dialCode + phonenumberCtl.text;
|
||||
AuthResult auth = await context.read<MainModel>().sendSms(phoneNumber);
|
||||
if (auth.authStatus == AuthStatus.SMS_SENT) {
|
||||
await Navigator.of(context).push(CupertinoPageRoute(
|
||||
builder: (context) => SmsCodePage(phoneNumber: phoneNumber)));
|
||||
builder: (context) => SmsCodePage(
|
||||
phoneNumber: phoneNumber,
|
||||
forceResendingToken: auth.forceResendingToken)));
|
||||
Navigator.pop(context);
|
||||
} else if (auth.authStatus == AuthStatus.AUTH_VERIFIED) {
|
||||
await navigateAfterAuthVerified(context);
|
||||
|
||||
@@ -12,13 +12,15 @@ import 'package:flutter/material.dart';
|
||||
import 'package:pin_input_text_field/pin_input_text_field.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import '../../constants.dart';
|
||||
import '../../helpers/theme.dart';
|
||||
|
||||
const resend_count_sec = 30;
|
||||
|
||||
class SmsCodePage extends StatefulWidget {
|
||||
final String phoneNumber;
|
||||
const SmsCodePage({Key? key, required this.phoneNumber}) : super(key: key);
|
||||
final String? forceResendingToken;
|
||||
const SmsCodePage(
|
||||
{Key? key, required this.phoneNumber, this.forceResendingToken})
|
||||
: super(key: key);
|
||||
@override
|
||||
_SmsCodePageState createState() => _SmsCodePageState();
|
||||
}
|
||||
@@ -26,16 +28,18 @@ class SmsCodePage extends StatefulWidget {
|
||||
class _SmsCodePageState extends State<SmsCodePage> {
|
||||
bool _isLoading = false;
|
||||
bool canResend = false;
|
||||
int _start = resend_count_sec;
|
||||
int _start = resendCountSec;
|
||||
|
||||
late String pin;
|
||||
late bool allNumberEntered;
|
||||
late Timer _timer;
|
||||
String? _forceResendingToken;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
pin = "";
|
||||
allNumberEntered = false;
|
||||
_forceResendingToken = widget.forceResendingToken;
|
||||
super.initState();
|
||||
startTimer();
|
||||
}
|
||||
@@ -186,7 +190,28 @@ class _SmsCodePageState extends State<SmsCodePage> {
|
||||
});
|
||||
}
|
||||
|
||||
_resend() async {}
|
||||
_resend() async {
|
||||
try {
|
||||
setState(() {
|
||||
_start = resendCountSec;
|
||||
canResend = false;
|
||||
});
|
||||
|
||||
_timer.cancel();
|
||||
startTimer();
|
||||
|
||||
var mainModel = context.read<MainModel>();
|
||||
|
||||
AuthResult auth = await mainModel.sendSms(widget.phoneNumber,
|
||||
forceResendingToken: _forceResendingToken);
|
||||
|
||||
if (auth.authStatus == AuthStatus.SMS_SENT) {
|
||||
_forceResendingToken = auth.forceResendingToken;
|
||||
}
|
||||
} catch (e) {
|
||||
await showMsgDialog(context, "Error", e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
_verify() async {
|
||||
setState(() {
|
||||
|
||||
188
lib/pages/widgets/input_phone.dart
Normal file
188
lib/pages/widgets/input_phone.dart
Normal file
@@ -0,0 +1,188 @@
|
||||
import 'package:fcs/helpers/theme.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:country_picker/country_picker.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import '../../localization/app_translations.dart';
|
||||
import '../main/model/language_model.dart';
|
||||
|
||||
typedef OnValueChange = Function(Country country);
|
||||
|
||||
class InputPhone extends StatefulWidget {
|
||||
final String? lableKey;
|
||||
final String? validationLabel;
|
||||
final OnValueChange onValueChange;
|
||||
final Country selectedCountry;
|
||||
final TextEditingController phoneCtl;
|
||||
final Function(String)? onInputChanged;
|
||||
final bool autofocus;
|
||||
|
||||
const InputPhone(
|
||||
{super.key,
|
||||
this.lableKey,
|
||||
required this.onValueChange,
|
||||
required this.selectedCountry,
|
||||
this.onInputChanged,
|
||||
required this.phoneCtl,
|
||||
this.validationLabel,
|
||||
this.autofocus = false});
|
||||
|
||||
@override
|
||||
State<InputPhone> createState() => _InputPhoneState();
|
||||
}
|
||||
|
||||
class _InputPhoneState extends State<InputPhone> {
|
||||
List<String>? signinCountries = [];
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
init();
|
||||
super.initState();
|
||||
}
|
||||
|
||||
init() {
|
||||
List<String>? countries = ['mm', 'us'];
|
||||
|
||||
for (var c in countries) {
|
||||
setState(() {
|
||||
signinCountries?.add(c.toUpperCase());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
bool isMyanmar = widget.selectedCountry.phoneCode == "95";
|
||||
var languageModel = Provider.of<LanguageModel>(context);
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(top: 10),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
showCountryPicker(
|
||||
context: context,
|
||||
countryFilter: signinCountries,
|
||||
showPhoneCode: true,
|
||||
showSearch: true,
|
||||
countryListTheme: const CountryListThemeData(
|
||||
flagSize: 25,
|
||||
backgroundColor: Colors.white,
|
||||
textStyle: TextStyle(fontSize: 15, color: Colors.black),
|
||||
bottomSheetHeight: 500,
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(20.0),
|
||||
topRight: Radius.circular(20.0),
|
||||
),
|
||||
inputDecoration: InputDecoration(
|
||||
labelText: 'Search',
|
||||
labelStyle: TextStyle(color: Colors.black),
|
||||
floatingLabelStyle: TextStyle(color: Colors.black),
|
||||
hintText: 'Search',
|
||||
hintStyle: TextStyle(color: Colors.black),
|
||||
hintMaxLines: 1,
|
||||
prefixIcon: Icon(Icons.search, color: Colors.black),
|
||||
border: OutlineInputBorder(),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide:
|
||||
BorderSide(width: 1, color: Colors.black)),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius:
|
||||
BorderRadius.all(Radius.circular(5.0)),
|
||||
borderSide: BorderSide(color: Colors.black)),
|
||||
),
|
||||
searchTextStyle: TextStyle(color: Colors.black)),
|
||||
onSelect: (Country country) {
|
||||
widget.onValueChange(country);
|
||||
},
|
||||
);
|
||||
},
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
width: 95,
|
||||
padding: const EdgeInsets.only(
|
||||
left: 5, bottom: 12, right: 5, top: 12),
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: Colors.grey.shade400),
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.fromLTRB(0, 0, 5, 0),
|
||||
child: Text(widget.selectedCountry.flagEmoji)),
|
||||
Text(
|
||||
isMyanmar
|
||||
? "+95"
|
||||
: "+${widget.selectedCountry.phoneCode}",
|
||||
style: const TextStyle(
|
||||
fontSize: 16, color: Colors.black),
|
||||
),
|
||||
const Icon(Icons.arrow_drop_down),
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 5),
|
||||
Flexible(
|
||||
child: Container(
|
||||
padding: const EdgeInsets.only(left: 10),
|
||||
child: TextFormField(
|
||||
autofocus: widget.autofocus,
|
||||
keyboardType: TextInputType.phone,
|
||||
onChanged: widget.onInputChanged,
|
||||
controller: widget.phoneCtl,
|
||||
maxLines: 1,
|
||||
cursorColor: primaryColor,
|
||||
style: const TextStyle(fontSize: 16, color: Colors.black),
|
||||
decoration: InputDecoration(
|
||||
labelStyle: languageModel.isEng
|
||||
? newLabelStyle(color: Colors.black54, fontSize: 20)
|
||||
: newLabelStyleMM(
|
||||
color: Colors.black54, fontSize: 20),
|
||||
labelText: widget.lableKey != null
|
||||
? AppTranslations.of(context)!
|
||||
.text(widget.lableKey!)
|
||||
: null,
|
||||
hintStyle: const TextStyle(color: Color(0xFFBBBBBB)),
|
||||
errorStyle: const TextStyle(color: dangerColor),
|
||||
prefix: Padding(
|
||||
padding: EdgeInsets.only(left: isMyanmar ? 0 : 13),
|
||||
child: Text(isMyanmar ? "9 " : "",
|
||||
style: const TextStyle(color: Colors.black))),
|
||||
enabledBorder: const UnderlineInputBorder(
|
||||
borderSide:
|
||||
BorderSide(color: primaryColor, width: 1.0)),
|
||||
focusedBorder: const UnderlineInputBorder(
|
||||
borderSide:
|
||||
BorderSide(color: primaryColor, width: 1.0)),
|
||||
errorBorder: null,
|
||||
focusedErrorBorder: null),
|
||||
validator: (value) {
|
||||
if (value!.isEmpty) {
|
||||
if (widget.validationLabel == null) return null;
|
||||
return AppTranslations.of(context)!
|
||||
.text(widget.validationLabel!);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -42,7 +42,6 @@ dependencies:
|
||||
connectivity_plus: ^6.1.0
|
||||
logging: ^1.0.1
|
||||
permission_handler: ^11.1.0
|
||||
country_code_picker: ^3.0.0
|
||||
pin_input_text_field: ^4.1.0
|
||||
flutter_icons_null_safety: ^1.1.0
|
||||
country_icons: ^2.0.2
|
||||
@@ -59,6 +58,7 @@ dependencies:
|
||||
qr_flutter: ^4.1.0
|
||||
flutter_markdown: ^0.6.20+1
|
||||
flutter_slidable: ^3.1.1
|
||||
country_picker: ^2.0.26
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
||||
Reference in New Issue
Block a user