From 0673ad1289989ef0b774b8b49677fb7ab13e5050 Mon Sep 17 00:00:00 2001 From: Cristian Hernandez Date: Mon, 16 Sep 2024 12:05:51 -0700 Subject: [PATCH 1/3] Fixes for issues found in QA: - Update angeleno site urls to new sandbox urls - Allow text to be highlighted in MFA TOTP modal - Update copyright date - Fixed unable to disable immediately after enabling #123 - Fixed scrollbar over text inputs and edit button on Profile page - Fixed error showing prior to user interaction. Disabled continue option until fields have values. --- functions/auth0/api/auth0.js | 71 ++-- lib/views/dialogs/authenticator.dart | 29 +- lib/views/dialogs/mobile.dart | 86 +++-- .../screens/advanced_security_screen.dart | 108 +++--- lib/views/screens/home_screen.dart | 11 +- lib/views/screens/profile_screen.dart | 356 +++++++++--------- 6 files changed, 340 insertions(+), 321 deletions(-) diff --git a/functions/auth0/api/auth0.js b/functions/auth0/api/auth0.js index d379d36..d5b3c4d 100644 --- a/functions/auth0/api/auth0.js +++ b/functions/auth0/api/auth0.js @@ -29,6 +29,7 @@ export const updateUser = onRequest(async (req, res) => { if (user.lastName) { updatedUserObject['family_name'] = user.lastName; + updatedUserObject['name'] += ` ${user.lastName}`; } const primaryAddress = {}; @@ -354,6 +355,40 @@ export const unenrollMFA = onRequest(async (req, res) => { } }); +export const removeConnection = onRequest(async (req, res) => { + try { + const { + connectionId + } = req.body; + + if (!connectionId) { + res.status(400).send('Invalid request - missing required fields.'); + return; + } + + const config = { + method: 'delete', + maxBodyLength: Infinity, + url: `https://${auth0Domain}/api/v2/grants/${connectionId}`, + headers: { + 'Authorization': `Bearer ${await getAccessToken()}` + } + }; + + await axios.request(config) + res.status(200).send(); + } catch (error) { + console.log(error); + + const { + status = 500, + message = '', + } = error.response; + + return res.status(status).send(message); + }; +}); + const getConnectedServices = async (userId) => { if (!userId) { @@ -427,38 +462,4 @@ const getConnectedServices = async (userId) => { return res.status(status).send(message); } -}; - -export const removeConnection = onRequest(async (req, res) => { - try { - const { - connectionId - } = req.body; - - if (!connectionId) { - res.status(400).send('Invalid request - missing required fields.'); - return; - } - - const config = { - method: 'delete', - maxBodyLength: Infinity, - url: `https://${auth0Domain}/api/v2/grants/${connectionId}`, - headers: { - 'Authorization': `Bearer ${await getAccessToken()}` - } - }; - - await axios.request(config) - res.status(200).send(); - } catch (error) { - console.log(error); - - const { - status = 500, - message = '', - } = error.response; - - return res.status(status).send(message); - }; -}); +}; \ No newline at end of file diff --git a/lib/views/dialogs/authenticator.dart b/lib/views/dialogs/authenticator.dart index 09da329..cca3ad8 100644 --- a/lib/views/dialogs/authenticator.dart +++ b/lib/views/dialogs/authenticator.dart @@ -36,7 +36,6 @@ class _AuthenticatorDialogState extends State { String qrCodeAltString = ''; String mfaToken = ''; - String userPassword = ''; bool obscurePassword = true; @override @@ -45,6 +44,10 @@ class _AuthenticatorDialogState extends State { userProvider = widget.userProvider; auth0UserApi = widget.auth0UserApi; + + passwordField.addListener(() { + setState(() {}); + }); } @override @@ -151,7 +154,7 @@ class _AuthenticatorDialogState extends State { children: [ dialogClose, TextButton( - onPressed: () { + onPressed: passwordField.text.isEmpty ? null : () { enrollTOTP(); }, child: const Text('Continue'), @@ -183,7 +186,7 @@ class _AuthenticatorDialogState extends State { obscureText: obscurePassword, enableSuggestions: false, autocorrect: false, - autovalidateMode: AutovalidateMode.always, + autovalidateMode: AutovalidateMode.onUserInteraction, validator: (final value) { if (value == null || value.trim().isEmpty) { return 'Password is required'; @@ -251,14 +254,14 @@ class _AuthenticatorDialogState extends State { const SizedBox(height: 20), const Text('If unable to scan, please enter the code below:'), const SizedBox(height: 15), - Text( - qrCodeAltString, - style: const TextStyle( - decoration: TextDecoration.none, - color: Colors.black, - fontSize: 14.0, - fontWeight: FontWeight.normal - ) + SelectableText( + qrCodeAltString, + style: const TextStyle( + decoration: TextDecoration.none, + color: Colors.black, + fontSize: 14.0, + fontWeight: FontWeight.normal + ) ) ], ), @@ -275,7 +278,7 @@ class _AuthenticatorDialogState extends State { children: [ dialogClose, TextButton( - onPressed: () { + onPressed: totpCode.isEmpty ? null : () { confirmTOTP(); }, child: const Text('Finish'), @@ -296,7 +299,7 @@ class _AuthenticatorDialogState extends State { child: TextFormField( key: const Key('totpCode'), autofocus: true, - autovalidateMode: AutovalidateMode.always, + autovalidateMode: AutovalidateMode.onUserInteraction, validator: (final value) { if (value == null || value.trim().isEmpty) { return 'Code is required'; diff --git a/lib/views/dialogs/mobile.dart b/lib/views/dialogs/mobile.dart index ccf6d0a..63c27e7 100644 --- a/lib/views/dialogs/mobile.dart +++ b/lib/views/dialogs/mobile.dart @@ -50,7 +50,7 @@ class _MobileDialogState extends State { bool validPhoneNumber = false; int _pageIndex = 0; - late List dialogNext; + // late List dialogNext; @override void initState() { @@ -60,31 +60,9 @@ class _MobileDialogState extends State { api = widget.userApi; channel = widget.channel; - dialogNext = [ - TextButton( - onPressed: () { - try { - if (!validPhoneNumber && isNotTestMode) { - return; - } - _navigateToNextPage(); - } catch (e) {} - }, - child: const Text('Continue'), - ), - TextButton( - onPressed: () { - enrollMobile(); - }, - child: const Text('Continue'), - ), - TextButton( - onPressed: () { - confirmCode(); - }, - child: const Text('Continue') - ) - ]; + passwordField.addListener(() { + setState(() {}); + }); } @override @@ -94,6 +72,32 @@ class _MobileDialogState extends State { super.dispose(); } + List get dialogNext => [ + TextButton( + onPressed: !validPhoneNumber && isNotTestMode ? null : () { + try { + if (!validPhoneNumber && isNotTestMode) { + return; + } + _navigateToNextPage(); + } catch (e) {} + }, + child: const Text('Continue'), + ), + TextButton( + onPressed: passwordField.text.isEmpty ? null : () { + enrollMobile(); + }, + child: const Text('Continue'), + ), + TextButton( + onPressed: codeProvided.isEmpty ? null : () { + confirmCode(); + }, + child: const Text('Continue') + ) + ]; + Widget get dialogClose => IconButton( alignment: Alignment.centerLeft, onPressed: () { @@ -216,7 +220,9 @@ class _MobileDialogState extends State { phoneNumber = number.phoneNumber!; }, onInputValidated: (final bool value) { - validPhoneNumber = value; + setState(() { + validPhoneNumber = value; + }); }, autoValidateMode: isNotTestMode ? AutovalidateMode.onUserInteraction @@ -256,7 +262,7 @@ class _MobileDialogState extends State { obscureText: obscurePassword, enableSuggestions: false, autocorrect: false, - autovalidateMode: AutovalidateMode.always, + autovalidateMode: AutovalidateMode.onUserInteraction, validator: (final value) { if (value == null || value.trim().isEmpty) { return 'Password is required'; @@ -264,17 +270,17 @@ class _MobileDialogState extends State { return null; }, decoration: InputDecoration( - suffixIcon: IconButton( - key: const Key('toggle_password'), - onPressed: () { - setState(() { - obscurePassword = !obscurePassword; - }); - }, - icon: Icon( - obscurePassword ? Icons.visibility : Icons.visibility_off - ), - ) + suffixIcon: IconButton( + key: const Key('toggle_password'), + onPressed: () { + setState(() { + obscurePassword = !obscurePassword; + }); + }, + icon: Icon( + obscurePassword ? Icons.visibility : Icons.visibility_off + ), + ) ), ), ), @@ -296,7 +302,7 @@ class _MobileDialogState extends State { child: TextFormField( key: const Key('phoneCode'), autofocus: true, - autovalidateMode: AutovalidateMode.always, + autovalidateMode: AutovalidateMode.onUserInteraction, validator: (final value) { if (value == null || value.trim().isEmpty) { return 'Code is required'; diff --git a/lib/views/screens/advanced_security_screen.dart b/lib/views/screens/advanced_security_screen.dart index 01069c6..c6a75ed 100644 --- a/lib/views/screens/advanced_security_screen.dart +++ b/lib/views/screens/advanced_security_screen.dart @@ -46,7 +46,13 @@ class _AdvancedSecurityState extends State { super.initState(); userProvider = widget.userProvider; auth0UserApi = widget.auth0UserApi; - _authMethods = getAuthenticationMethods(); + _triggerAuthMethods(); + } + + void _triggerAuthMethods() { + setState(() { + _authMethods = getAuthenticationMethods(); + }); } Future getAuthenticationMethods() async { @@ -158,12 +164,12 @@ class _AdvancedSecurityState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Semantics( - header: true, - child: const Text( - 'Multi-Factor Authentication', - textAlign: TextAlign.left, - style: headerStyle - ) + header: true, + child: const Text( + 'Multi-Factor Authentication', + textAlign: TextAlign.left, + style: headerStyle + ) ), const SizedBox(height: 10), Row( @@ -179,34 +185,34 @@ class _AdvancedSecurityState extends State { FilledButton.tonal( key: const Key('disableAuthenticator'), onPressed: () => showDialog( - context: context, - builder: (final BuildContext context) => AlertDialog( - title: const Text('Remove authenticator app?'), - content: const SingleChildScrollView( - child: ListBody( - children: [ - // ignore: avoid_escaping_inner_quotes - Text('You won\'t be able to use your ' - 'authenticator app to sign into your Angeleno ' - 'Account.') - ], - ) + context: context, + builder: (final BuildContext context) => AlertDialog( + title: const Text('Remove authenticator app?'), + content: const SingleChildScrollView( + child: ListBody( + children: [ + // ignore: avoid_escaping_inner_quotes + Text('You won\'t be able to use your ' + 'authenticator app to sign into your Angeleno ' + 'Account.') + ], + ) + ), + actions: [ + TextButton( + child: const Text('Cancel'), + onPressed: () { + Navigator.pop(context); + }, ), - actions: [ - TextButton( - child: const Text('Cancel'), - onPressed: () { - Navigator.pop(context); - }, - ), - TextButton( - child: const Text('Ok'), - onPressed: () { - disableMFA(totpAuthId, 'totp'); - }, - ) - ], - ) + TextButton( + child: const Text('Ok'), + onPressed: () { + disableMFA(totpAuthId, 'totp'); + }, + ) + ], + ) ).then((final value) { if (value != null && value == HttpStatus.ok) { setState(() { @@ -218,24 +224,22 @@ class _AdvancedSecurityState extends State { ) : FilledButton( - key: const Key('enableAuthenticator'), - onPressed: () { - showDialog( - context: context, - builder: (final BuildContext context) => - AuthenticatorDialog( - userProvider: userProvider, - auth0UserApi: auth0UserApi - ), - ).then((final value) { - if (value != null && value == HttpStatus.ok){ - setState(() { - authenticatorEnabled = true; - }); - } - }); - }, - child: const Text('Enable') + key: const Key('enableAuthenticator'), + onPressed: () { + showDialog( + context: context, + builder: (final BuildContext context) => + AuthenticatorDialog( + userProvider: userProvider, + auth0UserApi: auth0UserApi + ), + ).then((final value) { + if (value != null && value == HttpStatus.ok){ + _triggerAuthMethods(); + } + }); + }, + child: const Text('Enable') ), ], ), diff --git a/lib/views/screens/home_screen.dart b/lib/views/screens/home_screen.dart index d35aed6..7c44b95 100644 --- a/lib/views/screens/home_screen.dart +++ b/lib/views/screens/home_screen.dart @@ -24,6 +24,7 @@ class _MyHomePageState extends State { late User user; late OverlayProvider overlayProvider; int _selectedIndex = 0; + final currentYear = DateTime.now().year; @override void initState() { @@ -72,17 +73,17 @@ class _MyHomePageState extends State { switch(index) { case 3: await launchUrl( - Uri.parse('https://angeleno.lacity.org/') + Uri.parse('https://sandbox.account.lacity.gov/') ); break; case 4: await launchUrl( - Uri.parse('https://angeleno.lacity.org/apps') + Uri.parse('https://sandbox.account.lacity.gov/services') ); break; case 5: await launchUrl( - Uri.parse('https://angeleno.lacity.org/help') + Uri.parse('https://sandbox.account.lacity.gov/help') ); break; case 6: @@ -214,11 +215,11 @@ class _MyHomePageState extends State { ), bottomNavigationBar: Container( padding: const EdgeInsets.all(16.0), - child: const Wrap( + child: Wrap( alignment: WrapAlignment.center, children: [ Text( - '© Copyright 2023 City of Los Angeles. ' + '© Copyright $currentYear City of Los Angeles. ' 'All rights reserved. Disclaimer | Privacy Policy', textDirection: TextDirection.ltr, textAlign: TextAlign.center, diff --git a/lib/views/screens/profile_screen.dart b/lib/views/screens/profile_screen.dart index 8d1cf37..112563c 100644 --- a/lib/views/screens/profile_screen.dart +++ b/lib/views/screens/profile_screen.dart @@ -99,186 +99,190 @@ class _ProfileScreenState extends State { children: [ Expanded( child: SingleChildScrollView( - child: Form( - key: formKey, - onChanged: () { - formKey.currentState!.save(); - }, - autovalidateMode: AutovalidateMode.disabled, - child: Column( - children: [ - const SizedBox(height: 10.0), - Row(mainAxisAlignment: MainAxisAlignment.end, - children: [ - ElevatedButton( - onPressed: (editMode && - ((user.phone!.isNotEmpty && !validPhoneNumber) || - !isFormValid) && isNotTestMode - ) ? null : () { - if (editMode) { - updateUser(); - } - setState(() { - userProvider.toggleEditing(); - }); - }, - child: Text( - editMode ? 'Save' : 'Edit' - ), - ) - ]), - const SizedBox(height: 25.0), - TextFormField( - enabled: editMode, - decoration: inputDecoration('First Name', editMode), - style: textStyle(editMode), - initialValue: user.firstName, - maxLength: 300, - maxLengthEnforcement: MaxLengthEnforcement.enforced, - keyboardType: TextInputType.name, - autovalidateMode: AutovalidateMode.onUserInteraction, - validator: (final val) { - if (val == null || val.trim().isEmpty) { - return 'Please enter a first name'; - } + child: Padding( + padding: const EdgeInsets.only(right: 15.0), + child: Form( + key: formKey, + onChanged: () { + formKey.currentState!.save(); + }, + autovalidateMode: AutovalidateMode.disabled, + child: Column( + children: [ + const SizedBox(height: 10.0), + Row(mainAxisAlignment: MainAxisAlignment.end, + children: [ + ElevatedButton( + onPressed: (editMode && + ((user.phone!.isNotEmpty && !validPhoneNumber) || + !isFormValid) && isNotTestMode + ) ? null : () { + if (editMode) { + updateUser(); + } + setState(() { + userProvider.toggleEditing(); + }); + }, + child: Text( + editMode ? 'Save' : 'Edit' + ), + ) + ] + ), + const SizedBox(height: 25.0), + TextFormField( + enabled: editMode, + decoration: inputDecoration('First Name', editMode), + style: textStyle(editMode), + initialValue: user.firstName, + maxLength: 300, + maxLengthEnforcement: MaxLengthEnforcement.enforced, + keyboardType: TextInputType.name, + autovalidateMode: AutovalidateMode.onUserInteraction, + validator: (final val) { + if (val == null || val.trim().isEmpty) { + return 'Please enter a first name'; + } - if (!nameRegEx.hasMatch(val)) { - return 'Invalid characters in first name'; - } + if (!nameRegEx.hasMatch(val)) { + return 'Invalid characters in first name'; + } - return null; - }, - onChanged: (final val) { - setState(() { - user.firstName = val; - }); - }, - ), - const SizedBox(height: 25.0), - TextFormField( - enabled: editMode, - decoration: inputDecoration('Last Name', editMode), - style: textStyle(editMode), - initialValue: user.lastName, - maxLength: 150, - maxLengthEnforcement: MaxLengthEnforcement.enforced, - keyboardType: TextInputType.name, - autovalidateMode: AutovalidateMode.onUserInteraction, - validator: (final val) { - if (val == null || val.trim().isEmpty) { - return 'Please enter a last name'; - } + return null; + }, + onChanged: (final val) { + setState(() { + user.firstName = val; + }); + }, + ), + const SizedBox(height: 25.0), + TextFormField( + enabled: editMode, + decoration: inputDecoration('Last Name', editMode), + style: textStyle(editMode), + initialValue: user.lastName, + maxLength: 150, + maxLengthEnforcement: MaxLengthEnforcement.enforced, + keyboardType: TextInputType.name, + autovalidateMode: AutovalidateMode.onUserInteraction, + validator: (final val) { + if (val == null || val.trim().isEmpty) { + return 'Please enter a last name'; + } - if (!nameRegEx.hasMatch(val)) { - return 'Invalid characters in last name'; - } + if (!nameRegEx.hasMatch(val)) { + return 'Invalid characters in last name'; + } - return null; - }, - onChanged: (final val) { - setState(() { - user.lastName = val; - }); - }, - ), - const SizedBox(height: 25.0), - InternationalPhoneNumberInput( - selectorConfig: const SelectorConfig( - selectorType: PhoneInputSelectorType.DIALOG, - setSelectorButtonAsPrefixIcon: true, - leadingPadding: 20.0, - ), - isEnabled: editMode, - key: const Key('phoneField'), - onInputChanged: (final PhoneNumber number) { - if (number.parseNumber().isNotEmpty) { - user.phone = number.phoneNumber!; - } else { - user.phone = ''; - } - }, - onInputValidated: (final bool value) { - if (user.phone!.isEmpty) { - setState(() { - validPhoneNumber = true; - }); - } else { - if (validPhoneNumber != value) { - setState(() { - validPhoneNumber = value; - }); - } - } - }, - autoValidateMode: isNotTestMode ? - AutovalidateMode.onUserInteraction - : AutovalidateMode.disabled, - selectorTextStyle: const TextStyle(color: Colors.black), - initialValue: PhoneNumber(phoneNumber: user.phone, isoCode: 'US'), - keyboardType: const TextInputType.numberWithOptions( - signed: true, - decimal: true - ), - ignoreBlank: true, - inputBorder: const OutlineInputBorder(), - ), - const SizedBox(height: 25.0), - TextFormField( - enabled: editMode, - decoration: inputDecoration('Address', editMode), - style: textStyle(editMode), - keyboardType: TextInputType.streetAddress, - initialValue: user.address, - onChanged: (final val) { - user.address = val; - }, - ), - const SizedBox(height: 25.0), - TextFormField( - enabled: editMode, - decoration: inputDecoration('Address 2', editMode), - style: textStyle(editMode), - keyboardType: TextInputType.streetAddress, - initialValue: user.address2, - onChanged: (final val) { - user.address2 = val; - }, - ), - const SizedBox(height: 25.0), - TextFormField( - enabled: editMode, - decoration: inputDecoration('City', editMode), - style: textStyle(editMode), - keyboardType: TextInputType.streetAddress, - initialValue: user.city, - onChanged: (final val) { - user.city = val; - }, - ), - const SizedBox(height: 25.0), - TextFormField( - enabled: editMode, - decoration: inputDecoration('State', editMode), - style: textStyle(editMode), - keyboardType: TextInputType.streetAddress, - initialValue: user.state, - onChanged: (final val) { - user.state = val; - }, - ), - const SizedBox(height: 25.0), - TextFormField( - enabled: editMode, - decoration: inputDecoration('Zip', editMode), - style: textStyle(editMode), - initialValue: user.zip, - onChanged: (final val) { - user.zip = val; - }, - keyboardType: TextInputType.number, - ), - ], - ) + return null; + }, + onChanged: (final val) { + setState(() { + user.lastName = val; + }); + }, + ), + const SizedBox(height: 25.0), + InternationalPhoneNumberInput( + selectorConfig: const SelectorConfig( + selectorType: PhoneInputSelectorType.DIALOG, + setSelectorButtonAsPrefixIcon: true, + leadingPadding: 20.0, + ), + isEnabled: editMode, + key: const Key('phoneField'), + onInputChanged: (final PhoneNumber number) { + if (number.parseNumber().isNotEmpty) { + user.phone = number.phoneNumber!; + } else { + user.phone = ''; + } + }, + onInputValidated: (final bool value) { + if (user.phone!.isEmpty) { + setState(() { + validPhoneNumber = true; + }); + } else { + if (validPhoneNumber != value) { + setState(() { + validPhoneNumber = value; + }); + } + } + }, + autoValidateMode: isNotTestMode ? + AutovalidateMode.onUserInteraction + : AutovalidateMode.disabled, + selectorTextStyle: const TextStyle(color: Colors.black), + initialValue: PhoneNumber(phoneNumber: user.phone, isoCode: 'US'), + keyboardType: const TextInputType.numberWithOptions( + signed: true, + decimal: true + ), + ignoreBlank: true, + inputBorder: const OutlineInputBorder(), + ), + const SizedBox(height: 25.0), + TextFormField( + enabled: editMode, + decoration: inputDecoration('Address', editMode), + style: textStyle(editMode), + keyboardType: TextInputType.streetAddress, + initialValue: user.address, + onChanged: (final val) { + user.address = val; + }, + ), + const SizedBox(height: 25.0), + TextFormField( + enabled: editMode, + decoration: inputDecoration('Address 2', editMode), + style: textStyle(editMode), + keyboardType: TextInputType.streetAddress, + initialValue: user.address2, + onChanged: (final val) { + user.address2 = val; + }, + ), + const SizedBox(height: 25.0), + TextFormField( + enabled: editMode, + decoration: inputDecoration('City', editMode), + style: textStyle(editMode), + keyboardType: TextInputType.streetAddress, + initialValue: user.city, + onChanged: (final val) { + user.city = val; + }, + ), + const SizedBox(height: 25.0), + TextFormField( + enabled: editMode, + decoration: inputDecoration('State', editMode), + style: textStyle(editMode), + keyboardType: TextInputType.streetAddress, + initialValue: user.state, + onChanged: (final val) { + user.state = val; + }, + ), + const SizedBox(height: 25.0), + TextFormField( + enabled: editMode, + decoration: inputDecoration('Zip', editMode), + style: textStyle(editMode), + initialValue: user.zip, + onChanged: (final val) { + user.zip = val; + }, + keyboardType: TextInputType.number, + ), + ], + ) + ) ) ) ) From 37def9e47aa4e023ee356612ddc41948efa90119 Mon Sep 17 00:00:00 2001 From: Cristian Hernandez Date: Mon, 16 Sep 2024 13:26:47 -0700 Subject: [PATCH 2/3] Fix tests. Update logout url --- lib/controllers/user_provider.dart | 2 +- test/advanced_test.dart | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/controllers/user_provider.dart b/lib/controllers/user_provider.dart index 22344b6..8ac9e55 100644 --- a/lib/controllers/user_provider.dart +++ b/lib/controllers/user_provider.dart @@ -96,7 +96,7 @@ class UserProvider extends ChangeNotifier { Future logout() => auth0Web.logout( federated: false, - returnToUrl: 'https://angeleno.lacity.org/' + returnToUrl: 'https://sandbox.account.lacity.gov/' ); User? get user => _user; diff --git a/test/advanced_test.dart b/test/advanced_test.dart index 358413b..c46e247 100644 --- a/test/advanced_test.dart +++ b/test/advanced_test.dart @@ -144,7 +144,7 @@ void main() { await tester.pumpAndSettle(); await tester.enterText(find.byKey(const Key('totpCode')), '123456'); - + await tester.pump(); await tester.tap(find.widgetWithText(TextButton, 'Finish')); await tester.pumpAndSettle(); @@ -211,6 +211,7 @@ void main() { await tester.pumpAndSettle(); await tester.enterText(find.byKey(const Key('phoneCode')), '483234'); + await tester.pump(); await tester.tap(find.widgetWithText(TextButton, 'Continue')); await tester.pumpAndSettle(); @@ -237,10 +238,12 @@ void main() { await tester.pumpAndSettle(); await tester.enterText(find.byKey(const Key('passwordField')), 'myPassword'); + await tester.pump(); await tester.tap(find.widgetWithText(TextButton, 'Continue')); await tester.pumpAndSettle(); await tester.enterText(find.byKey(const Key('phoneCode')), '483234'); + await tester.pump(); await tester.tap(find.widgetWithText(TextButton, 'Continue')); await tester.pumpAndSettle(); From 5fcaa3fb3877290ed39d5be9b846c42a895772b6 Mon Sep 17 00:00:00 2001 From: Cristian Hernandez Date: Mon, 16 Sep 2024 13:47:32 -0700 Subject: [PATCH 3/3] Changes made in Sandbox and not dev --- .env-example | 3 +++ .github/workflows/firebase-hosting-merge.yml | 1 + .github/workflows/firebase-hosting-pull-request.yml | 1 + lib/controllers/auth0_user_api.dart | 2 ++ lib/controllers/auth0_user_api_implementation.dart | 1 + lib/main.dart | 3 ++- lib/utils/constants.dart | 1 + lib/views/dialogs/mobile.dart | 3 --- 8 files changed, 11 insertions(+), 4 deletions(-) diff --git a/.env-example b/.env-example index 2edf704..427f5d0 100644 --- a/.env-example +++ b/.env-example @@ -10,5 +10,8 @@ CLOUD_FUNCTIONS_URL= SA_EMAIL= SA_SECRET_KEY= +#Development Environment +ENVIRONMENT=development + # Places API PLACES_API_KEY= diff --git a/.github/workflows/firebase-hosting-merge.yml b/.github/workflows/firebase-hosting-merge.yml index d52354f..e628958 100644 --- a/.github/workflows/firebase-hosting-merge.yml +++ b/.github/workflows/firebase-hosting-merge.yml @@ -31,6 +31,7 @@ jobs: echo CLOUD_FUNCTIONS_URL=${{ secrets.CLOUD_FUNCTIONS_URL }} >> .env echo SA_EMAIL=${{ secrets.SA_EMAIL }} >> .env echo SA_SECRET_KEY="${{ secrets.SA_SECRET_KEY }}" >> .env + echo ENVIRONMENT="${{ secrets.ENVIRONMENT }}" >> .env - run: flutter build web --dart-define-from-file=.env - name: Update .firebaserc run: | diff --git a/.github/workflows/firebase-hosting-pull-request.yml b/.github/workflows/firebase-hosting-pull-request.yml index cbeb649..e591339 100644 --- a/.github/workflows/firebase-hosting-pull-request.yml +++ b/.github/workflows/firebase-hosting-pull-request.yml @@ -35,6 +35,7 @@ jobs: echo CLOUD_FUNCTIONS_URL=${{ secrets.CLOUD_FUNCTIONS_URL }} >> .env echo SA_EMAIL=${{ secrets.SA_EMAIL }} >> .env echo SA_SECRET_KEY="${{ secrets.SA_SECRET_KEY }}" >> .env + echo ENVIRONMENT="${{ secrets.ENVIRONMENT }}" >> .env - run: flutter build web --dart-define-from-file=.env - name: Update .firebaserc run: | diff --git a/lib/controllers/auth0_user_api.dart b/lib/controllers/auth0_user_api.dart index 6e97deb..9cd2ce8 100644 --- a/lib/controllers/auth0_user_api.dart +++ b/lib/controllers/auth0_user_api.dart @@ -17,4 +17,6 @@ abstract class Api { void confirmMFA(final Map body); void unenrollMFA(final Map body); + + void removeConnection(final String connectionId); } \ No newline at end of file diff --git a/lib/controllers/auth0_user_api_implementation.dart b/lib/controllers/auth0_user_api_implementation.dart index cdc2453..fd0277a 100644 --- a/lib/controllers/auth0_user_api_implementation.dart +++ b/lib/controllers/auth0_user_api_implementation.dart @@ -302,6 +302,7 @@ class Auth0UserApi extends Api { } } + @override Future removeConnection(final String connectionId) async { final token = await getOAuthToken(); diff --git a/lib/main.dart b/lib/main.dart index f371838..80d0048 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -26,7 +26,8 @@ class MyApp extends StatelessWidget { @override Widget build(final BuildContext context) => MaterialApp( - title: 'Angeleno - Account', + title: 'Angeleno - My Account ' + '${environment == 'prod' ? '' : ' - $environment'}', debugShowCheckedModeBanner: false, theme: ThemeData( useMaterial3: true, diff --git a/lib/utils/constants.dart b/lib/utils/constants.dart index bc64a2e..782d772 100644 --- a/lib/utils/constants.dart +++ b/lib/utils/constants.dart @@ -8,6 +8,7 @@ const cloudFunctionURL = String.fromEnvironment('CLOUD_FUNCTIONS_URL'); const serviceAccountSecret = String.fromEnvironment('SA_SECRET_KEY'); const serviceAccountEmail = String.fromEnvironment('SA_EMAIL'); +const environment = String.fromEnvironment('ENVIRONMENT'); /* Regex */ final RegExp nameRegEx = RegExp(r"^[a-zA-ZÀ-ÿ\s'\-\d]*$"); diff --git a/lib/views/dialogs/mobile.dart b/lib/views/dialogs/mobile.dart index 63c27e7..04a7c94 100644 --- a/lib/views/dialogs/mobile.dart +++ b/lib/views/dialogs/mobile.dart @@ -42,7 +42,6 @@ class _MobileDialogState extends State { String errMsg = ''; String phoneNumber = ''; - String userPassword = ''; String mfaToken = ''; String oobCode = ''; String codeProvided = ''; @@ -50,8 +49,6 @@ class _MobileDialogState extends State { bool validPhoneNumber = false; int _pageIndex = 0; - // late List dialogNext; - @override void initState() { super.initState();