SMS phone verification

This commit is contained in:
Hillel Coren 2022-11-06 17:55:49 +02:00
parent 8f49a5f8f0
commit 44287dcfb4
4 changed files with 91 additions and 6 deletions

View File

@ -421,6 +421,48 @@ class _MenuDrawerState extends State<MenuDrawer> {
}, },
), ),
) )
else if (state.user.isTwoFactorEnabled &&
!state.user.phoneVerified &&
state.isHosted)
if (state.isMenuCollapsed)
Tooltip(
message:
localization.verifyPhoneNumber2faHelp,
child: ListTile(
contentPadding:
const EdgeInsets.only(left: 12),
leading: IconButton(
onPressed: () {
showDialog<void>(
context: context,
builder: (BuildContext context) =>
UserSmsVerification(
showChangeNumber: true,
),
);
},
icon: Icon(Icons.warning,
color: Colors.orange),
),
),
)
else
Material(
child: ListTile(
tileColor: Colors.orange.shade800,
subtitle: Text(
localization.verifyPhoneNumber2faHelp,
style: TextStyle(color: Colors.white),
),
onTap: () {
showDialog<void>(
context: context,
builder: (BuildContext context) =>
UserSmsVerification(),
);
},
),
)
else if (state.company.isDisabled && else if (state.company.isDisabled &&
state.userCompany.isAdmin) state.userCompany.isAdmin)
if (state.isMenuCollapsed) if (state.isMenuCollapsed)

View File

@ -9,6 +9,7 @@ import 'package:invoiceninja_flutter/constants.dart';
import 'package:invoiceninja_flutter/data/web_client.dart'; import 'package:invoiceninja_flutter/data/web_client.dart';
import 'package:invoiceninja_flutter/redux/app/app_actions.dart'; import 'package:invoiceninja_flutter/redux/app/app_actions.dart';
import 'package:invoiceninja_flutter/redux/app/app_state.dart'; import 'package:invoiceninja_flutter/redux/app/app_state.dart';
import 'package:invoiceninja_flutter/redux/settings/settings_actions.dart';
import 'package:invoiceninja_flutter/ui/app/forms/app_form.dart'; import 'package:invoiceninja_flutter/ui/app/forms/app_form.dart';
import 'package:invoiceninja_flutter/ui/app/loading_indicator.dart'; import 'package:invoiceninja_flutter/ui/app/loading_indicator.dart';
import 'package:invoiceninja_flutter/ui/app/pinput.dart'; import 'package:invoiceninja_flutter/ui/app/pinput.dart';
@ -188,7 +189,12 @@ class _AccountSmsVerificationState extends State<AccountSmsVerification> {
} }
class UserSmsVerification extends StatefulWidget { class UserSmsVerification extends StatefulWidget {
const UserSmsVerification({Key key}) : super(key: key); const UserSmsVerification({
Key key,
this.showChangeNumber = false,
}) : super(key: key);
final bool showChangeNumber;
@override @override
State<UserSmsVerification> createState() => _UserSmsVerificationState(); State<UserSmsVerification> createState() => _UserSmsVerificationState();
@ -268,7 +274,7 @@ class _UserSmsVerificationState extends State<UserSmsVerification> {
_isLoading = false; _isLoading = false;
}); });
if (navigator.canPop()) { if (navigator.canPop()) {
navigator.pop(); navigator.pop(true);
} }
showToast(localization.verifiedPhoneNumber); showToast(localization.verifiedPhoneNumber);
store.dispatch(RefreshData()); store.dispatch(RefreshData());
@ -289,6 +295,8 @@ class _UserSmsVerificationState extends State<UserSmsVerification> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final localization = AppLocalization.of(context); final localization = AppLocalization.of(context);
final store = StoreProvider.of<AppState>(context);
final state = store.state;
return AlertDialog( return AlertDialog(
title: Text(localization.verifyPhoneNumber), title: Text(localization.verifyPhoneNumber),
@ -301,7 +309,8 @@ class _UserSmsVerificationState extends State<UserSmsVerification> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
Text(localization.codeWasSent), Text(localization.codeWasSentTo
.replaceFirst(':number', state.user.phone)),
SizedBox(height: 20), SizedBox(height: 20),
AppPinput( AppPinput(
onCompleted: (code) => _code = code, onCompleted: (code) => _code = code,
@ -317,6 +326,15 @@ class _UserSmsVerificationState extends State<UserSmsVerification> {
), ),
), ),
if (!_isLoading) ...[ if (!_isLoading) ...[
TextButton(
onPressed: () {
store.dispatch(ViewSettings(section: kSettingsUserDetails));
Navigator.of(context).pop();
},
child: Text(
localization.changeNumber.toUpperCase(),
),
),
TextButton( TextButton(
onPressed: () => _sendCode(), onPressed: () => _sendCode(),
child: Text( child: Text(

View File

@ -384,7 +384,7 @@ class _UserDetailsState extends State<UserDetails>
.toUpperCase(), .toUpperCase(),
textAlign: TextAlign.center, textAlign: TextAlign.center,
), ),
onPressed: () { onPressed: () async {
if (state.settingsUIState.isChanged) { if (state.settingsUIState.isChanged) {
showMessageDialog( showMessageDialog(
context: context, context: context,
@ -406,11 +406,19 @@ class _UserDetailsState extends State<UserDetails>
if (!kReleaseMode && if (!kReleaseMode &&
(state.isHosted && !state.user.phoneVerified)) { (state.isHosted && !state.user.phoneVerified)) {
showDialog<void>( final bool phoneVerified = await showDialog<bool>(
context: context, context: context,
builder: (BuildContext context) => builder: (BuildContext context) =>
UserSmsVerification(), UserSmsVerification(),
); );
if (phoneVerified) {
showDialog<void>(
context: context,
builder: (BuildContext context) =>
_EnableTwoFactor(state: viewModel.state),
);
}
} else { } else {
showDialog<void>( showDialog<void>(
context: context, context: context,

View File

@ -16,6 +16,7 @@ mixin LocalizationsProvider on LocaleCodeAware {
static final Map<String, Map<String, String>> _localizedValues = { static final Map<String, Map<String, String>> _localizedValues = {
'en': { 'en': {
// STARTER: lang key - do not remove comment // STARTER: lang key - do not remove comment
'change_number': 'Change Number',
'resend_code': 'Resend Code', 'resend_code': 'Resend Code',
'base_type': 'Base Type', 'base_type': 'Base Type',
'category_type': 'Category Type', 'category_type': 'Category Type',
@ -119,6 +120,7 @@ mixin LocalizationsProvider on LocaleCodeAware {
'invoice_item_tax_rates': 'Invoice Item Tax Rates', 'invoice_item_tax_rates': 'Invoice Item Tax Rates',
'verified_phone_number': 'Successfully verified phone number', 'verified_phone_number': 'Successfully verified phone number',
'code_was_sent': 'A code has been sent via SMS', 'code_was_sent': 'A code has been sent via SMS',
'code_was_sent_to': 'A code has been sent via SMS to :number',
'resend': 'Resend', 'resend': 'Resend',
'verify': 'Verify', 'verify': 'Verify',
'enter_phone_number': 'Please provide a phone number', 'enter_phone_number': 'Please provide a phone number',
@ -126,6 +128,8 @@ mixin LocalizationsProvider on LocaleCodeAware {
'verify_phone_number': 'Verify Phone Number', 'verify_phone_number': 'Verify Phone Number',
'verify_phone_number_help': 'verify_phone_number_help':
'Please verify your phone number to send emails', 'Please verify your phone number to send emails',
'verify_phone_number_2fa_help':
'Please verify your phone number for 2FA backup',
'merged_clients': 'Successfully merged clients', 'merged_clients': 'Successfully merged clients',
'merge_into': 'Merge Into', 'merge_into': 'Merge Into',
'merge': 'Merge', 'merge': 'Merge',
@ -87471,6 +87475,19 @@ mixin LocalizationsProvider on LocaleCodeAware {
_localizedValues[localeCode]['resend_code'] ?? _localizedValues[localeCode]['resend_code'] ??
_localizedValues['en']['resend_code']; _localizedValues['en']['resend_code'];
String get verifyPhoneNumber2faHelp =>
_localizedValues[localeCode]['verify_phone_number_2fa_help'] ??
_localizedValues['en']['verify_phone_number_2fa_help'];
String get codeWasSentTo =>
_localizedValues[localeCode]['code_was_sent_to'] ??
_localizedValues['en']['code_was_sent_to'];
String get changeNumber =>
_localizedValues[localeCode]['change_number'] ??
_localizedValues['en']['change_number'];
// STARTER: lang field - do not remove comment // STARTER: lang field - do not remove comment
String lookup(String key) { String lookup(String key) {