Skip to content

Commit

Permalink
feat: display message after resendSignUpCode
Browse files Browse the repository at this point in the history
  • Loading branch information
Jordan-Nelson committed Nov 5, 2021
1 parent 1d3d2c9 commit 8c373c2
Show file tree
Hide file tree
Showing 12 changed files with 350 additions and 12 deletions.
34 changes: 31 additions & 3 deletions packages/amplify_authenticator/lib/amplify_authenticator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import 'package:amplify_authenticator/src/enums/enums.dart';
import 'package:amplify_authenticator/src/keys.dart';
import 'package:amplify_authenticator/src/l10n/auth_strings_resolver.dart';
import 'package:amplify_authenticator/src/l10n/authenticator_localizations.dart';
import 'package:amplify_authenticator/src/l10n/string_resolver.dart';
import 'package:amplify_authenticator/src/models/authenticator_exception.dart';
import 'package:amplify_authenticator/src/screens/authenticator_screen.dart';
import 'package:amplify_authenticator/src/screens/loading_screen.dart';
Expand All @@ -33,7 +34,7 @@ import 'package:amplify_authenticator/src/state/inherited_config.dart';
import 'package:amplify_authenticator/src/state/inherited_forms.dart';
import 'package:amplify_authenticator/src/state/inherited_strings.dart';
import 'package:amplify_authenticator/src/theme/amplify_theme.dart';
import 'package:amplify_authenticator/src/widgets/exception_banner.dart';
import 'package:amplify_authenticator/src/widgets/authenticator_banner.dart';
import 'package:amplify_authenticator/src/widgets/form.dart';
import 'package:amplify_core/amplify_core.dart';
import 'package:amplify_flutter/src/config/amplify_config.dart';
Expand Down Expand Up @@ -197,16 +198,21 @@ class _AuthenticatorState extends State<Authenticator> {
late final StateMachineBloc _stateMachineBloc;
late final AuthViewModel _viewModel;
late final StreamSubscription<AuthenticatorException> _exceptionSub;
late final StreamSubscription<StringResolver> _infoSub;
AmplifyConfig? _config;
late List<String> _missingConfigValues;
bool _configInitialized = false;

@override
void initState() {
super.initState();
_stateMachineBloc = StateMachineBloc(_authService)..add(const AuthLoad());
_stateMachineBloc = StateMachineBloc(
authService: _authService,
authStringResolver: widget.stringResolver,
)..add(const AuthLoad());
_viewModel = AuthViewModel(_stateMachineBloc);
_subscribeToExceptions();
_subscribeToInfoMessages();
_waitForConfiguration();
}

Expand Down Expand Up @@ -235,9 +241,31 @@ class _AuthenticatorState extends State<Authenticator> {
});
}

void _subscribeToInfoMessages() {
_infoSub = _stateMachineBloc.infoMessages.listen((resolver) {
if (mounted) {
ScaffoldMessenger.of(context)
..clearMaterialBanners()
..showMaterialBanner(createMaterialBanner(
type: StatusType.info,
content: Text(resolver(context)),
margin: MediaQuery.of(context).viewPadding.top,
actions: [
IconButton(
onPressed: () =>
ScaffoldMessenger.of(context).clearMaterialBanners(),
icon: const Icon(Icons.close),
),
],
));
}
});
}

@override
void dispose() {
_exceptionSub.cancel();
_infoSub.cancel();
_stateMachineBloc.dispose();
super.dispose();
}
Expand Down Expand Up @@ -308,7 +336,7 @@ class _AuthenticatorState extends State<Authenticator> {
}

class _AuthenticatorBody extends StatelessWidget {
_AuthenticatorBody({
const _AuthenticatorBody({
Key? key,
required this.child,
}) : super(key: key);
Expand Down
32 changes: 30 additions & 2 deletions packages/amplify_authenticator/lib/src/blocs/auth/auth_bloc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import 'dart:async';
import 'package:amplify_auth_cognito/amplify_auth_cognito.dart';
import 'package:amplify_authenticator/amplify_authenticator.dart';
import 'package:amplify_authenticator/src/blocs/auth/auth_data.dart';
import 'package:amplify_authenticator/src/l10n/string_resolver.dart';
import 'package:amplify_authenticator/src/services/amplify_auth_service.dart';
import 'package:amplify_flutter/amplify.dart';
import 'package:amplify_flutter/src/config/amplify_config.dart';
Expand All @@ -32,6 +33,7 @@ part 'auth_state.dart';
/// {@endtemplate}
class StateMachineBloc {
final AuthService _authService;
final AuthStringResolver _authStringResolver;

/// State controller.
final StreamController<AuthState> _authStateController =
Expand All @@ -58,7 +60,11 @@ class StateMachineBloc {
AuthState get currentState => _currentState;

/// {@macro authenticator.state_machine_bloc}
StateMachineBloc(this._authService) {
StateMachineBloc({
required AuthService authService,
required AuthStringResolver authStringResolver,
}) : _authService = authService,
_authStringResolver = authStringResolver {
_subscription =
_authEventStream.asyncExpand(_eventTransformer).listen((state) {
_controllerSink.add(state);
Expand All @@ -78,6 +84,13 @@ class StateMachineBloc {
/// Exception events which occur in the bloc.
Stream<AuthenticatorException> get exceptions => _exceptionController.stream;

/// Manages info messages separate from the bloc's state.
final StreamController<StringResolver> _infoMessageController =
StreamController<StringResolver>.broadcast();

/// Info messages generated from the bloc.
Stream<StringResolver> get infoMessages => _infoMessageController.stream;

Stream<AuthState> _eventTransformer(AuthEvent event) async* {
if (event is AuthLoad) {
yield* _authLoad();
Expand Down Expand Up @@ -375,7 +388,22 @@ class StateMachineBloc {

Stream<AuthState> _resendSignUpCode(String username) async* {
try {
await _authService.resendSignUpCode(username);
ResendSignUpCodeResult result =
await _authService.resendSignUpCode(username);
String? deliveryMedium = result.codeDeliveryDetails.deliveryMedium;
switch (deliveryMedium) {
case 'EMAIL':
_infoMessageController
.add(_authStringResolver.messages.codeSentEmail);
break;
case 'SMS':
_infoMessageController.add(_authStringResolver.messages.codeSentSMS);
break;
default:
_infoMessageController.add(
_authStringResolver.messages.codeSentUnknown,
);
}
yield VerificationCodeSent((_currentState as AuthFlow).screen);
} on AmplifyException catch (e) {
_exceptionController.add(AuthenticatorException(e.message));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import 'package:flutter/material.dart';

import 'button_resolver.dart';
import 'input_resolver.dart';
import 'message_resolver.dart';
import 'title_resolver.dart';

export 'button_resolver.dart';
Expand All @@ -40,14 +41,19 @@ class AuthStringResolver {
/// The resolver class for titles
final TitleResolver titles;

/// The resolver class for titles
final MessageResolver messages;

/// {@macro authenticator.auth_string_resolver}
const AuthStringResolver({
ButtonResolver? buttons,
InputResolver? inputs,
TitleResolver? titles,
MessageResolver? messages,
}) : titles = titles ?? const TitleResolver(),
buttons = buttons ?? const ButtonResolver(),
inputs = inputs ?? const InputResolver();
inputs = inputs ?? const InputResolver(),
messages = messages ?? const MessageResolver();

@override
bool operator ==(Object other) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@

import 'package:amplify_authenticator/src/l10n/generated/button_localizations_en.dart';
import 'package:amplify_authenticator/src/l10n/generated/input_localizations_en.dart';
import 'package:amplify_authenticator/src/l10n/generated/message_localizations_en.dart';
import 'package:amplify_authenticator/src/l10n/generated/title_localizations_en.dart';

import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';

import 'generated/button_localizations.dart';
import 'generated/input_localizations.dart';
import 'generated/message_localizations.dart';
import 'generated/title_localizations.dart';

/// Reference class for all Authenticator localizations.
Expand All @@ -41,6 +43,7 @@ abstract class AuthenticatorLocalizations {
static final _buttonsFallback = AuthenticatorButtonLocalizationsEn();
static final _inputsFallback = AuthenticatorInputLocalizationsEn();
static final _titlesFallback = AuthenticatorTitleLocalizationsEn();
static final _messagesFallback = AuthenticatorMessageLocalizationsEn();

/// Retrieves the [AuthenticatorButtonLocalizations] instance, falling back
/// to English if unavailable for this locale.
Expand All @@ -59,4 +62,10 @@ abstract class AuthenticatorLocalizations {
static AuthenticatorTitleLocalizations titlesOf(BuildContext context) {
return AuthenticatorTitleLocalizations.of(context) ?? _titlesFallback;
}

/// Retrieves the [AuthenticatorMessageLocalizations] instance, falling back
/// to English if unavailable for this locale.
static AuthenticatorMessageLocalizations messagesOf(BuildContext context) {
return AuthenticatorMessageLocalizations.of(context) ?? _messagesFallback;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
/*
* Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/

import 'dart:async';

import 'package:flutter/widgets.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:intl/intl.dart' as intl;

import 'message_localizations_en.dart' deferred as message_localizations_en;

/// Callers can lookup localized strings with an instance of AuthenticatorMessageLocalizations returned
/// by `AuthenticatorMessageLocalizations.of(context)`.
///
/// Applications need to include `AuthenticatorMessageLocalizations.delegate()` in their app's
/// localizationDelegates list, and the locales they support in the app's
/// supportedLocales list. For example:
///
/// ```
/// import 'generated/message_localizations.dart';
///
/// return MaterialApp(
/// localizationsDelegates: AuthenticatorMessageLocalizations.localizationsDelegates,
/// supportedLocales: AuthenticatorMessageLocalizations.supportedLocales,
/// home: MyApplicationHome(),
/// );
/// ```
///
/// ## Update pubspec.yaml
///
/// Please make sure to update your pubspec.yaml to include the following
/// packages:
///
/// ```
/// dependencies:
/// # Internationalization support.
/// flutter_localizations:
/// sdk: flutter
/// intl: any # Use the pinned version from flutter_localizations
///
/// # rest of dependencies
/// ```
///
/// ## iOS Applications
///
/// iOS applications define key application metadata, including supported
/// locales, in an Info.plist file that is built into the application bundle.
/// To configure the locales supported by your app, you’ll need to edit this
/// file.
///
/// First, open your project’s ios/Runner.xcworkspace Xcode workspace file.
/// Then, in the Project Navigator, open the Info.plist file under the Runner
/// project’s Runner folder.
///
/// Next, select the Information Property List item, select Add Item from the
/// Editor menu, then select Localizations from the pop-up menu.
///
/// Select and expand the newly-created Localizations item then, for each
/// locale your application supports, add a new item and select the locale
/// you wish to add from the pop-up menu in the Value field. This list should
/// be consistent with the languages listed in the AuthenticatorMessageLocalizations.supportedLocales
/// property.
abstract class AuthenticatorMessageLocalizations {
AuthenticatorMessageLocalizations(String locale)
: localeName = intl.Intl.canonicalizedLocale(locale.toString());

final String localeName;

static AuthenticatorMessageLocalizations? of(BuildContext context) {
return Localizations.of<AuthenticatorMessageLocalizations>(
context, AuthenticatorMessageLocalizations);
}

static const LocalizationsDelegate<AuthenticatorMessageLocalizations>
delegate = _AuthenticatorMessageLocalizationsDelegate();

/// A list of this localizations delegate along with the default localizations
/// delegates.
///
/// Returns a list of localizations delegates containing this delegate along with
/// GlobalMaterialLocalizations.delegate, GlobalCupertinoLocalizations.delegate,
/// and GlobalWidgetsLocalizations.delegate.
///
/// Additional delegates can be added by appending to this list in
/// MaterialApp. This list does not have to be used at all if a custom list
/// of delegates is preferred or required.
static const List<LocalizationsDelegate<dynamic>> localizationsDelegates =
<LocalizationsDelegate<dynamic>>[
delegate,
GlobalMaterialLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
];

/// A list of this localizations delegate's supported locales.
static const List<Locale> supportedLocales = <Locale>[Locale('en')];

/// The message that is displayed after a new confirmation code is sent via email
///
/// In en, this message translates to:
/// **'A new confirmation code has been sent to the email associated with this account.'**
String get codeSentEmail;

/// The message that is displayed after a new confirmation code is sent via SMS
///
/// In en, this message translates to:
/// **'A new confirmation code has been sent to the phone number associated with this account.'**
String get codeSentSMS;

/// The message that is displayed after a new confirmation code is sent via an unknown delivery medium
///
/// In en, this message translates to:
/// **'A new confirmation code has been sent.'**
String get codeSentUnknown;
}

class _AuthenticatorMessageLocalizationsDelegate
extends LocalizationsDelegate<AuthenticatorMessageLocalizations> {
const _AuthenticatorMessageLocalizationsDelegate();

@override
Future<AuthenticatorMessageLocalizations> load(Locale locale) {
return lookupAuthenticatorMessageLocalizations(locale);
}

@override
bool isSupported(Locale locale) =>
<String>['en'].contains(locale.languageCode);

@override
bool shouldReload(_AuthenticatorMessageLocalizationsDelegate old) => false;
}

Future<AuthenticatorMessageLocalizations>
lookupAuthenticatorMessageLocalizations(Locale locale) {
// Lookup logic when only language code is specified.
switch (locale.languageCode) {
case 'en':
return message_localizations_en.loadLibrary().then((dynamic _) =>
message_localizations_en.AuthenticatorMessageLocalizationsEn());
}

throw FlutterError(
'AuthenticatorMessageLocalizations.delegate failed to load unsupported locale "$locale". This is likely '
'an issue with the localizations generation tool. Please file an issue '
'on GitHub with a reproducible sample app and the gen-l10n configuration '
'that was used.');
}
Loading

0 comments on commit 8c373c2

Please sign in to comment.