diff --git a/modules/legacy/modules/legacy_auth/lib/src/features/login/presentation/login_screen.dart b/modules/legacy/modules/legacy_auth/lib/src/features/login/presentation/login_screen.dart index 103950ad..46af71d2 100644 --- a/modules/legacy/modules/legacy_auth/lib/src/features/login/presentation/login_screen.dart +++ b/modules/legacy/modules/legacy_auth/lib/src/features/login/presentation/login_screen.dart @@ -1,13 +1,14 @@ -import 'package:legacy_auth/src/features/login/domain/entities/legacy_auth_error_event.dart'; -import 'package:legacy_theme/legacy_theme.dart'; -import 'package:legacy_auth/src/features/login/presentation/state/login_view_model.dart'; -import 'package:legacy_auth/src/features/login/presentation/widgets/field_error_text.dart'; -import 'package:legacy_auth/src/features/login/presentation/widgets/two_factor_sheet_launcher.dart'; import 'package:design_system/design_system.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:legacy_auth/src/features/login/domain/entities/legacy_auth_error_event.dart'; +import 'package:legacy_auth/src/features/login/presentation/providers/login_controller.dart'; +import 'package:legacy_auth/src/features/login/presentation/widgets/field_error_text.dart'; +import 'package:legacy_auth/src/features/login/presentation/widgets/two_factor_sheet_launcher.dart'; +import 'package:legacy_theme/legacy_theme.dart'; import 'package:navigation/navigation.dart'; import 'package:sf_localizations/sf_localizations.dart'; +import 'package:sf_shared/sf_shared.dart'; String _authErrorI18nKey(LegacyAuthErrorEvent event) { return switch (event) { @@ -24,19 +25,62 @@ String _authErrorI18nKey(LegacyAuthErrorEvent event) { }; } -class LegacyLoginScreen extends ConsumerWidget { +class LegacyLoginScreen extends ConsumerStatefulWidget { final NavigationContract navigationContract; const LegacyLoginScreen({super.key, required this.navigationContract}); @override - Widget build(BuildContext context, WidgetRef ref) { - final bool isLoading = ref.watch( - legacyLoginViewModelProvider.select((s) => s.isLoading), - ); + ConsumerState createState() => _LegacyLoginScreenState(); +} + +class _LegacyLoginScreenState extends ConsumerState { + late final TextEditingController _emailController; + late final TextEditingController _passwordController; + + @override + void initState() { + super.initState(); + final initial = ref.read(loginControllerProvider); + _emailController = TextEditingController(text: initial.email); + _passwordController = TextEditingController(text: initial.password); + _emailController.addListener(_onEmailChanged); + _passwordController.addListener(_onPasswordChanged); + } + + @override + void dispose() { + _emailController.removeListener(_onEmailChanged); + _passwordController.removeListener(_onPasswordChanged); + _emailController.dispose(); + _passwordController.dispose(); + super.dispose(); + } + + void _onEmailChanged() { + ref + .read(loginControllerProvider.notifier) + .setEmail(_emailController.text); + } + + void _onPasswordChanged() { + ref + .read(loginControllerProvider.notifier) + .setPassword(_passwordController.text); + } + + void _submit() { + FocusManager.instance.primaryFocus?.unfocus(); + ref.read(loginControllerProvider.notifier).login(); + } + + @override + Widget build(BuildContext context) { + final isLoading = + ref.watch(loginControllerProvider.select((s) => s.isLoading)); ref.listen( - legacyLoginViewModelProvider.select( + loginControllerProvider.select( (s) => ( errorEvent: s.errorEvent, twoFA: s.twoFARequested, @@ -44,29 +88,20 @@ class LegacyLoginScreen extends ConsumerWidget { hasDevices: s.hasDevices, ), ), - (previous, next) { + (previous, next) async { final errorEvent = next.errorEvent; if (errorEvent != null && !next.twoFA) { - showTopSnackbar( - context, - message: context.translate(_authErrorI18nKey(errorEvent)), - type: MessageType.error, - ); - ref.read(legacyLoginViewModelProvider.notifier).clearErrorEvent(); + await showErrorDialog(context, _authErrorI18nKey(errorEvent)); + ref.read(loginControllerProvider.notifier).clearErrorEvent(); } if (next.twoFA && previous?.twoFA != true) { - showTwoFactorSheet(context); + if (context.mounted) showTwoFactorSheet(context); } if (next.verified) { - showTopSnackbar( - context, - message: context.translate(I18n.loginSuccess), - type: MessageType.success, - ); if (next.hasDevices) { - navigationContract.goTo(AppRoutes.controlPanel); + widget.navigationContract.goTo(AppRoutes.controlPanel); } else { - navigationContract.goTo(AppRoutes.legacyDeviceSetup); + widget.navigationContract.goTo(AppRoutes.legacyDeviceSetup); } } }, @@ -78,31 +113,22 @@ class LegacyLoginScreen extends ConsumerWidget { child: AbsorbPointer( absorbing: isLoading, child: SingleChildScrollView( - padding: EdgeInsets.symmetric(horizontal: 24), + padding: const EdgeInsets.symmetric(horizontal: 24), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ const _Header(), - SizedBox(height: 48), - _EmailSection(), - SizedBox(height: 24), + const SizedBox(height: 48), + _EmailSection(controller: _emailController), + const SizedBox(height: 24), _PasswordSection( - onSubmitted: () { - FocusManager.instance.primaryFocus?.unfocus(); - ref.read(legacyLoginViewModelProvider.notifier).login(); - }, + controller: _passwordController, + onSubmitted: _submit, ), - SizedBox(height: 16), - // _ForgotPassword(navigationContract: navigationContract), - // SizedBox(height: 30), - _SignInSection( - onSignIn: () { - FocusManager.instance.primaryFocus?.unfocus(); - ref.read(legacyLoginViewModelProvider.notifier).login(); - }, - ), - SizedBox(height: 30), - _Footer(navigationContract: navigationContract), + const SizedBox(height: 16), + _SignInSection(onSignIn: _submit), + const SizedBox(height: 30), + _Footer(navigationContract: widget.navigationContract), ], ), ), @@ -135,14 +161,13 @@ class _Header extends StatelessWidget { } class _EmailSection extends ConsumerWidget { - const _EmailSection(); + const _EmailSection({required this.controller}); + final TextEditingController controller; @override Widget build(BuildContext context, WidgetRef ref) { - final vm = ref.read(legacyLoginViewModelProvider.notifier); - final String emailErrorKey = ref.watch( - legacyLoginViewModelProvider.select((s) => s.emailError), - ); + final emailErrorKey = + ref.watch(loginControllerProvider.select((s) => s.emailError)); return Column( crossAxisAlignment: CrossAxisAlignment.stretch, @@ -150,7 +175,7 @@ class _EmailSection extends ConsumerWidget { CustomTextField( hint: context.translate(I18n.username), label: context.translate(I18n.username), - controller: vm.emailController, + controller: controller, keyboardType: TextInputType.emailAddress, textInputAction: TextInputAction.next, ), @@ -161,18 +186,19 @@ class _EmailSection extends ConsumerWidget { } class _PasswordSection extends ConsumerWidget { - const _PasswordSection({required this.onSubmitted}); + const _PasswordSection({ + required this.controller, + required this.onSubmitted, + }); + final TextEditingController controller; final VoidCallback onSubmitted; @override Widget build(BuildContext context, WidgetRef ref) { - final vm = ref.read(legacyLoginViewModelProvider.notifier); - final bool passwordVisible = ref.watch( - legacyLoginViewModelProvider.select((s) => s.passwordVisible), - ); - final String passwordErrorKey = ref.watch( - legacyLoginViewModelProvider.select((s) => s.passwordError), - ); + final passwordVisible = + ref.watch(loginControllerProvider.select((s) => s.passwordVisible)); + final passwordErrorKey = + ref.watch(loginControllerProvider.select((s) => s.passwordError)); return Column( crossAxisAlignment: CrossAxisAlignment.stretch, @@ -181,7 +207,7 @@ class _PasswordSection extends ConsumerWidget { showPassword: passwordVisible, label: context.translate(I18n.password), hint: '********', - controller: vm.passwordController, + controller: controller, textInputAction: TextInputAction.done, onSubmitted: (_) => onSubmitted(), ), @@ -191,15 +217,15 @@ class _PasswordSection extends ConsumerWidget { } } +// ignore: unused_element class _ForgotPassword extends ConsumerWidget { const _ForgotPassword({required this.navigationContract}); final NavigationContract navigationContract; @override Widget build(BuildContext context, WidgetRef ref) { - final bool isLoading = ref.watch( - legacyLoginViewModelProvider.select((s) => s.isLoading), - ); + final isLoading = + ref.watch(loginControllerProvider.select((s) => s.isLoading)); return Align( alignment: Alignment.topLeft, @@ -221,9 +247,8 @@ class _SignInSection extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final bool isLoading = ref.watch( - legacyLoginViewModelProvider.select((s) => s.isLoading), - ); + final isLoading = + ref.watch(loginControllerProvider.select((s) => s.isLoading)); return Column( crossAxisAlignment: CrossAxisAlignment.stretch, @@ -254,9 +279,8 @@ class _Footer extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final bool isLoading = ref.watch( - legacyLoginViewModelProvider.select((s) => s.isLoading), - ); + final isLoading = + ref.watch(loginControllerProvider.select((s) => s.isLoading)); return Column( children: [ diff --git a/modules/legacy/modules/legacy_auth/lib/src/features/login/presentation/state/login_view_model.dart b/modules/legacy/modules/legacy_auth/lib/src/features/login/presentation/providers/login_controller.dart similarity index 61% rename from modules/legacy/modules/legacy_auth/lib/src/features/login/presentation/state/login_view_model.dart rename to modules/legacy/modules/legacy_auth/lib/src/features/login/presentation/providers/login_controller.dart index 625a1471..c6092171 100644 --- a/modules/legacy/modules/legacy_auth/lib/src/features/login/presentation/state/login_view_model.dart +++ b/modules/legacy/modules/legacy_auth/lib/src/features/login/presentation/providers/login_controller.dart @@ -1,65 +1,40 @@ import 'dart:async'; -import 'package:legacy_auth/src/core/domain/repositories/login_repository.dart'; +import 'package:flutter/widgets.dart'; import 'package:legacy_auth/src/core/providers/login_repository_provider.dart'; import 'package:legacy_auth/src/features/login/domain/entities/legacy_auth_error_event.dart'; import 'package:legacy_auth/src/features/login/presentation/mixins/login_form_validation.dart'; -import 'package:legacy_auth/src/features/login/presentation/state/login_view_state.dart'; -import 'package:flutter/widgets.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:legacy_auth/src/features/login/presentation/providers/login_state.dart'; +import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:sf_infrastructure/sf_infrastructure.dart'; import 'package:sf_shared/sf_shared.dart'; import 'package:sf_tracking/sf_tracking.dart'; -final legacyLoginViewModelProvider = - NotifierProvider.autoDispose( - LegacyLoginViewModel.new, - ); +part 'login_controller.g.dart'; -class LegacyLoginViewModel extends Notifier - with LoginFormValidation { - late final LegacyLoginRepository _repository; - late final GetUserInfoUseCase _getUserInfoUseCase; - late final NotificationsRemoteDatasource _notifications; - late final SfTrackingRepository _tracking; - - late final TextEditingController emailController; - late final TextEditingController passwordController; +const int resendCooldownSeconds = 30; +@Riverpod(keepAlive: true) +class LoginController extends _$LoginController with LoginFormValidation { Timer? _cooldownTimer; - static const int resendCooldownSeconds = 30; - @override - LegacyLoginViewState build() { - _repository = ref.read(legacyLoginRepositoryProvider); - _getUserInfoUseCase = ref.read(getUserInfoUseCaseProvider); - _notifications = ref.read(notificationsRemoteDatasourceProvider); - _tracking = ref.read(sfTrackingProvider); - - emailController = TextEditingController(); - emailController.addListener(_onEmailChanged); - - passwordController = TextEditingController(); - passwordController.addListener(_onPasswordChanged); - - ref.onDispose(_dispose); - - return const LegacyLoginViewState(); + LoginState build() { + ref.onDispose(() => _cooldownTimer?.cancel()); + return const LoginState(); } - void _onEmailChanged() { - final value = emailController.text; + void setEmail(String value) { if (value == state.email) return; state = state.copyWith( email: value, errorEvent: null, - emailError: state.showErrors ? validateEmail(value) : state.emailError, + emailError: + state.showErrors ? validateEmail(value) : state.emailError, ); } - void _onPasswordChanged() { - final value = passwordController.text; + void setPassword(String value) { if (value == state.password) return; state = state.copyWith( password: value, @@ -70,14 +45,6 @@ class LegacyLoginViewModel extends Notifier ); } - void _dispose() { - _cooldownTimer?.cancel(); - emailController.removeListener(_onEmailChanged); - emailController.dispose(); - passwordController.removeListener(_onPasswordChanged); - passwordController.dispose(); - } - void togglePasswordVisible() { state = state.copyWith(passwordVisible: !state.passwordVisible); } @@ -105,6 +72,8 @@ class LegacyLoginViewModel extends Notifier final email = state.email.trim(); final password = state.password.trim(); + final repository = ref.read(legacyLoginRepositoryProvider); + final tracking = ref.read(sfTrackingProvider); state = state.copyWith( isLoading: true, @@ -112,17 +81,12 @@ class LegacyLoginViewModel extends Notifier twoFAVerified: false, ); - unawaited(_tracking.legacyAuthLoginAttempt()); + unawaited(tracking.legacyAuthLoginAttempt()); try { - final response = await _repository.login( - email: email, - password: password, - ); + final response = await repository.login(email: email, password: password); - if (!ref.mounted) return; - - unawaited(_tracking.legacyAuthLoginSuccess()); + unawaited(tracking.legacyAuthLoginSuccess()); state = state.copyWith( token: response.token, @@ -130,10 +94,7 @@ class LegacyLoginViewModel extends Notifier code: '', ); } catch (e) { - if (!ref.mounted) return; - - unawaited(_tracking.legacyAuthLoginFailure(formatErrorMessage(e))); - + unawaited(tracking.legacyAuthLoginFailure(formatErrorMessage(e))); state = state.copyWith( isLoading: false, errorEvent: mapLoginError(e), @@ -161,15 +122,16 @@ class LegacyLoginViewModel extends Notifier return; } + final repository = ref.read(legacyLoginRepositoryProvider); + final tracking = ref.read(sfTrackingProvider); + try { - await _repository.twoFARequestCode( + await repository.twoFARequestCode( token: state.token, methodType: state.availableMethods.first.methodType, ); - if (!ref.mounted) return; - - unawaited(_tracking.legacyAuth2faRequested()); + unawaited(tracking.legacyAuth2faRequested()); _startResendCooldown(); state = state.copyWith( @@ -177,8 +139,6 @@ class LegacyLoginViewModel extends Notifier isLoading: updateLoading ? false : state.isLoading, ); } catch (e) { - if (!ref.mounted) return; - state = state.copyWith( errorEvent: mapTwoFactorError(e), isLoading: updateLoading ? false : state.isLoading, @@ -214,32 +174,31 @@ class LegacyLoginViewModel extends Notifier orElse: () => state.availableMethods.first, ); + final repository = ref.read(legacyLoginRepositoryProvider); + final tracking = ref.read(sfTrackingProvider); + final getUserInfoUseCase = ref.read(getUserInfoUseCaseProvider); + final notifications = ref.read(notificationsRemoteDatasourceProvider); + state = state.copyWith(isLoading: true, errorEvent: null, codeError: ''); try { - await _repository.twoFASendCode( + await repository.twoFASendCode( token: state.token, code: state.code.trim(), methodType: method.methodType, ); - if (!ref.mounted) return; + unawaited(tracking.legacyAuth2faVerified()); - unawaited(_tracking.legacyAuth2faVerified()); - - await _getUserInfoUseCase.getUserInfo(); - - if (!ref.mounted) return; + await getUserInfoUseCase.getUserInfo(); unawaited( - _notifications.registerCurrentPushToken().catchError((Object e) { + notifications.registerCurrentPushToken().catchError((Object e) { debugPrint('[FCM] failed to register push token post-login: $e'); }), ); - final hasDevices = await _repository.hasDevices(); - - if (!ref.mounted) return; + final hasDevices = await repository.hasDevices(); state = state.copyWith( isLoading: false, @@ -247,10 +206,7 @@ class LegacyLoginViewModel extends Notifier hasDevices: hasDevices, ); } catch (e) { - if (!ref.mounted) return; - - unawaited(_tracking.legacyAuth2faFailure(formatErrorMessage(e))); - + unawaited(tracking.legacyAuth2faFailure(formatErrorMessage(e))); state = state.copyWith( isLoading: false, errorEvent: mapTwoFactorError(e), @@ -259,7 +215,7 @@ class LegacyLoginViewModel extends Notifier } Future resendCode() async { - unawaited(_tracking.legacyAuth2faResend()); + unawaited(ref.read(sfTrackingProvider).legacyAuth2faResend()); state = state.copyWith(code: '', isLoading: true); await _requestTwoFACode(updateLoading: true); } diff --git a/modules/legacy/modules/legacy_auth/lib/src/features/login/presentation/providers/login_controller.g.dart b/modules/legacy/modules/legacy_auth/lib/src/features/login/presentation/providers/login_controller.g.dart new file mode 100644 index 00000000..c4f208a9 --- /dev/null +++ b/modules/legacy/modules/legacy_auth/lib/src/features/login/presentation/providers/login_controller.g.dart @@ -0,0 +1,63 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'login_controller.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint, type=warning + +@ProviderFor(LoginController) +const loginControllerProvider = LoginControllerProvider._(); + +final class LoginControllerProvider + extends $NotifierProvider { + const LoginControllerProvider._() + : super( + from: null, + argument: null, + retry: null, + name: r'loginControllerProvider', + isAutoDispose: false, + dependencies: null, + $allTransitiveDependencies: null, + ); + + @override + String debugGetCreateSourceHash() => _$loginControllerHash(); + + @$internal + @override + LoginController create() => LoginController(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(LoginState value) { + return $ProviderOverride( + origin: this, + providerOverride: $SyncValueProvider(value), + ); + } +} + +String _$loginControllerHash() => r'991fa84bd8aba5a53640ca0443163f54de175e45'; + +abstract class _$LoginController extends $Notifier { + LoginState build(); + @$mustCallSuper + @override + void runBuild() { + final created = build(); + final ref = this.ref as $Ref; + final element = + ref.element + as $ClassProviderElement< + AnyNotifier, + LoginState, + Object?, + Object? + >; + element.handleValue(ref, created); + } +} diff --git a/modules/legacy/modules/legacy_auth/lib/src/features/login/presentation/state/login_view_state.dart b/modules/legacy/modules/legacy_auth/lib/src/features/login/presentation/providers/login_state.dart similarity index 83% rename from modules/legacy/modules/legacy_auth/lib/src/features/login/presentation/state/login_view_state.dart rename to modules/legacy/modules/legacy_auth/lib/src/features/login/presentation/providers/login_state.dart index d8c9bc44..251546e2 100644 --- a/modules/legacy/modules/legacy_auth/lib/src/features/login/presentation/state/login_view_state.dart +++ b/modules/legacy/modules/legacy_auth/lib/src/features/login/presentation/providers/login_state.dart @@ -1,12 +1,12 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:legacy_auth/src/features/login/domain/entities/legacy_auth_error_event.dart'; import 'package:legacy_auth/src/features/login/domain/entities/login_response_entity.dart'; -import 'package:freezed_annotation/freezed_annotation.dart'; -part 'login_view_state.freezed.dart'; +part 'login_state.freezed.dart'; @freezed -abstract class LegacyLoginViewState with _$LegacyLoginViewState { - const factory LegacyLoginViewState({ +abstract class LoginState with _$LoginState { + const factory LoginState({ @Default('') String email, @Default('') String password, @Default(false) bool passwordVisible, @@ -24,5 +24,5 @@ abstract class LegacyLoginViewState with _$LegacyLoginViewState { @Default(0) int resendCooldown, @Default(false) bool twoFAVerified, @Default(false) bool hasDevices, - }) = _LegacyLoginViewState; + }) = _LoginState; } diff --git a/modules/legacy/modules/legacy_auth/lib/src/features/login/presentation/state/login_view_state.freezed.dart b/modules/legacy/modules/legacy_auth/lib/src/features/login/presentation/providers/login_state.freezed.dart similarity index 68% rename from modules/legacy/modules/legacy_auth/lib/src/features/login/presentation/state/login_view_state.freezed.dart rename to modules/legacy/modules/legacy_auth/lib/src/features/login/presentation/providers/login_state.freezed.dart index 4bfbc05e..0b87027d 100644 --- a/modules/legacy/modules/legacy_auth/lib/src/features/login/presentation/state/login_view_state.freezed.dart +++ b/modules/legacy/modules/legacy_auth/lib/src/features/login/presentation/providers/login_state.freezed.dart @@ -3,7 +3,7 @@ // ignore_for_file: type=lint // ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark -part of 'login_view_state.dart'; +part of 'login_state.dart'; // ************************************************************************** // FreezedGenerator @@ -12,20 +12,20 @@ part of 'login_view_state.dart'; // dart format off T _$identity(T value) => value; /// @nodoc -mixin _$LegacyLoginViewState { +mixin _$LoginState { String get email; String get password; bool get passwordVisible; String get emailError; String get passwordError; LegacyAuthErrorEvent? get errorEvent; bool get showErrors; bool get isLoading; List get availableMethods; String get token; bool get twoFARequested; String get code; String get codeError; int get resendCooldown; bool get twoFAVerified; bool get hasDevices; -/// Create a copy of LegacyLoginViewState +/// Create a copy of LoginState /// with the given fields replaced by the non-null parameter values. @JsonKey(includeFromJson: false, includeToJson: false) @pragma('vm:prefer-inline') -$LegacyLoginViewStateCopyWith get copyWith => _$LegacyLoginViewStateCopyWithImpl(this as LegacyLoginViewState, _$identity); +$LoginStateCopyWith get copyWith => _$LoginStateCopyWithImpl(this as LoginState, _$identity); @override bool operator ==(Object other) { - return identical(this, other) || (other.runtimeType == runtimeType&&other is LegacyLoginViewState&&(identical(other.email, email) || other.email == email)&&(identical(other.password, password) || other.password == password)&&(identical(other.passwordVisible, passwordVisible) || other.passwordVisible == passwordVisible)&&(identical(other.emailError, emailError) || other.emailError == emailError)&&(identical(other.passwordError, passwordError) || other.passwordError == passwordError)&&(identical(other.errorEvent, errorEvent) || other.errorEvent == errorEvent)&&(identical(other.showErrors, showErrors) || other.showErrors == showErrors)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&const DeepCollectionEquality().equals(other.availableMethods, availableMethods)&&(identical(other.token, token) || other.token == token)&&(identical(other.twoFARequested, twoFARequested) || other.twoFARequested == twoFARequested)&&(identical(other.code, code) || other.code == code)&&(identical(other.codeError, codeError) || other.codeError == codeError)&&(identical(other.resendCooldown, resendCooldown) || other.resendCooldown == resendCooldown)&&(identical(other.twoFAVerified, twoFAVerified) || other.twoFAVerified == twoFAVerified)&&(identical(other.hasDevices, hasDevices) || other.hasDevices == hasDevices)); + return identical(this, other) || (other.runtimeType == runtimeType&&other is LoginState&&(identical(other.email, email) || other.email == email)&&(identical(other.password, password) || other.password == password)&&(identical(other.passwordVisible, passwordVisible) || other.passwordVisible == passwordVisible)&&(identical(other.emailError, emailError) || other.emailError == emailError)&&(identical(other.passwordError, passwordError) || other.passwordError == passwordError)&&(identical(other.errorEvent, errorEvent) || other.errorEvent == errorEvent)&&(identical(other.showErrors, showErrors) || other.showErrors == showErrors)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&const DeepCollectionEquality().equals(other.availableMethods, availableMethods)&&(identical(other.token, token) || other.token == token)&&(identical(other.twoFARequested, twoFARequested) || other.twoFARequested == twoFARequested)&&(identical(other.code, code) || other.code == code)&&(identical(other.codeError, codeError) || other.codeError == codeError)&&(identical(other.resendCooldown, resendCooldown) || other.resendCooldown == resendCooldown)&&(identical(other.twoFAVerified, twoFAVerified) || other.twoFAVerified == twoFAVerified)&&(identical(other.hasDevices, hasDevices) || other.hasDevices == hasDevices)); } @@ -34,15 +34,15 @@ int get hashCode => Object.hash(runtimeType,email,password,passwordVisible,email @override String toString() { - return 'LegacyLoginViewState(email: $email, password: $password, passwordVisible: $passwordVisible, emailError: $emailError, passwordError: $passwordError, errorEvent: $errorEvent, showErrors: $showErrors, isLoading: $isLoading, availableMethods: $availableMethods, token: $token, twoFARequested: $twoFARequested, code: $code, codeError: $codeError, resendCooldown: $resendCooldown, twoFAVerified: $twoFAVerified, hasDevices: $hasDevices)'; + return 'LoginState(email: $email, password: $password, passwordVisible: $passwordVisible, emailError: $emailError, passwordError: $passwordError, errorEvent: $errorEvent, showErrors: $showErrors, isLoading: $isLoading, availableMethods: $availableMethods, token: $token, twoFARequested: $twoFARequested, code: $code, codeError: $codeError, resendCooldown: $resendCooldown, twoFAVerified: $twoFAVerified, hasDevices: $hasDevices)'; } } /// @nodoc -abstract mixin class $LegacyLoginViewStateCopyWith<$Res> { - factory $LegacyLoginViewStateCopyWith(LegacyLoginViewState value, $Res Function(LegacyLoginViewState) _then) = _$LegacyLoginViewStateCopyWithImpl; +abstract mixin class $LoginStateCopyWith<$Res> { + factory $LoginStateCopyWith(LoginState value, $Res Function(LoginState) _then) = _$LoginStateCopyWithImpl; @useResult $Res call({ String email, String password, bool passwordVisible, String emailError, String passwordError, LegacyAuthErrorEvent? errorEvent, bool showErrors, bool isLoading, List availableMethods, String token, bool twoFARequested, String code, String codeError, int resendCooldown, bool twoFAVerified, bool hasDevices @@ -53,14 +53,14 @@ $Res call({ } /// @nodoc -class _$LegacyLoginViewStateCopyWithImpl<$Res> - implements $LegacyLoginViewStateCopyWith<$Res> { - _$LegacyLoginViewStateCopyWithImpl(this._self, this._then); +class _$LoginStateCopyWithImpl<$Res> + implements $LoginStateCopyWith<$Res> { + _$LoginStateCopyWithImpl(this._self, this._then); - final LegacyLoginViewState _self; - final $Res Function(LegacyLoginViewState) _then; + final LoginState _self; + final $Res Function(LoginState) _then; -/// Create a copy of LegacyLoginViewState +/// Create a copy of LoginState /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({Object? email = null,Object? password = null,Object? passwordVisible = null,Object? emailError = null,Object? passwordError = null,Object? errorEvent = freezed,Object? showErrors = null,Object? isLoading = null,Object? availableMethods = null,Object? token = null,Object? twoFARequested = null,Object? code = null,Object? codeError = null,Object? resendCooldown = null,Object? twoFAVerified = null,Object? hasDevices = null,}) { return _then(_self.copyWith( @@ -87,8 +87,8 @@ as bool, } -/// Adds pattern-matching-related methods to [LegacyLoginViewState]. -extension LegacyLoginViewStatePatterns on LegacyLoginViewState { +/// Adds pattern-matching-related methods to [LoginState]. +extension LoginStatePatterns on LoginState { /// A variant of `map` that fallback to returning `orElse`. /// /// It is equivalent to doing: @@ -101,10 +101,10 @@ extension LegacyLoginViewStatePatterns on LegacyLoginViewState { /// } /// ``` -@optionalTypeArgs TResult maybeMap(TResult Function( _LegacyLoginViewState value)? $default,{required TResult orElse(),}){ +@optionalTypeArgs TResult maybeMap(TResult Function( _LoginState value)? $default,{required TResult orElse(),}){ final _that = this; switch (_that) { -case _LegacyLoginViewState() when $default != null: +case _LoginState() when $default != null: return $default(_that);case _: return orElse(); @@ -123,10 +123,10 @@ return $default(_that);case _: /// } /// ``` -@optionalTypeArgs TResult map(TResult Function( _LegacyLoginViewState value) $default,){ +@optionalTypeArgs TResult map(TResult Function( _LoginState value) $default,){ final _that = this; switch (_that) { -case _LegacyLoginViewState(): +case _LoginState(): return $default(_that);case _: throw StateError('Unexpected subclass'); @@ -144,10 +144,10 @@ return $default(_that);case _: /// } /// ``` -@optionalTypeArgs TResult? mapOrNull(TResult? Function( _LegacyLoginViewState value)? $default,){ +@optionalTypeArgs TResult? mapOrNull(TResult? Function( _LoginState value)? $default,){ final _that = this; switch (_that) { -case _LegacyLoginViewState() when $default != null: +case _LoginState() when $default != null: return $default(_that);case _: return null; @@ -167,7 +167,7 @@ return $default(_that);case _: @optionalTypeArgs TResult maybeWhen(TResult Function( String email, String password, bool passwordVisible, String emailError, String passwordError, LegacyAuthErrorEvent? errorEvent, bool showErrors, bool isLoading, List availableMethods, String token, bool twoFARequested, String code, String codeError, int resendCooldown, bool twoFAVerified, bool hasDevices)? $default,{required TResult orElse(),}) {final _that = this; switch (_that) { -case _LegacyLoginViewState() when $default != null: +case _LoginState() when $default != null: return $default(_that.email,_that.password,_that.passwordVisible,_that.emailError,_that.passwordError,_that.errorEvent,_that.showErrors,_that.isLoading,_that.availableMethods,_that.token,_that.twoFARequested,_that.code,_that.codeError,_that.resendCooldown,_that.twoFAVerified,_that.hasDevices);case _: return orElse(); @@ -188,7 +188,7 @@ return $default(_that.email,_that.password,_that.passwordVisible,_that.emailErro @optionalTypeArgs TResult when(TResult Function( String email, String password, bool passwordVisible, String emailError, String passwordError, LegacyAuthErrorEvent? errorEvent, bool showErrors, bool isLoading, List availableMethods, String token, bool twoFARequested, String code, String codeError, int resendCooldown, bool twoFAVerified, bool hasDevices) $default,) {final _that = this; switch (_that) { -case _LegacyLoginViewState(): +case _LoginState(): return $default(_that.email,_that.password,_that.passwordVisible,_that.emailError,_that.passwordError,_that.errorEvent,_that.showErrors,_that.isLoading,_that.availableMethods,_that.token,_that.twoFARequested,_that.code,_that.codeError,_that.resendCooldown,_that.twoFAVerified,_that.hasDevices);case _: throw StateError('Unexpected subclass'); @@ -208,7 +208,7 @@ return $default(_that.email,_that.password,_that.passwordVisible,_that.emailErro @optionalTypeArgs TResult? whenOrNull(TResult? Function( String email, String password, bool passwordVisible, String emailError, String passwordError, LegacyAuthErrorEvent? errorEvent, bool showErrors, bool isLoading, List availableMethods, String token, bool twoFARequested, String code, String codeError, int resendCooldown, bool twoFAVerified, bool hasDevices)? $default,) {final _that = this; switch (_that) { -case _LegacyLoginViewState() when $default != null: +case _LoginState() when $default != null: return $default(_that.email,_that.password,_that.passwordVisible,_that.emailError,_that.passwordError,_that.errorEvent,_that.showErrors,_that.isLoading,_that.availableMethods,_that.token,_that.twoFARequested,_that.code,_that.codeError,_that.resendCooldown,_that.twoFAVerified,_that.hasDevices);case _: return null; @@ -220,8 +220,8 @@ return $default(_that.email,_that.password,_that.passwordVisible,_that.emailErro /// @nodoc -class _LegacyLoginViewState implements LegacyLoginViewState { - const _LegacyLoginViewState({this.email = '', this.password = '', this.passwordVisible = false, this.emailError = '', this.passwordError = '', this.errorEvent, this.showErrors = false, this.isLoading = false, final List availableMethods = const [], this.token = '', this.twoFARequested = false, this.code = '', this.codeError = '', this.resendCooldown = 0, this.twoFAVerified = false, this.hasDevices = false}): _availableMethods = availableMethods; +class _LoginState implements LoginState { + const _LoginState({this.email = '', this.password = '', this.passwordVisible = false, this.emailError = '', this.passwordError = '', this.errorEvent, this.showErrors = false, this.isLoading = false, final List availableMethods = const [], this.token = '', this.twoFARequested = false, this.code = '', this.codeError = '', this.resendCooldown = 0, this.twoFAVerified = false, this.hasDevices = false}): _availableMethods = availableMethods; @override@JsonKey() final String email; @@ -247,17 +247,17 @@ class _LegacyLoginViewState implements LegacyLoginViewState { @override@JsonKey() final bool twoFAVerified; @override@JsonKey() final bool hasDevices; -/// Create a copy of LegacyLoginViewState +/// Create a copy of LoginState /// with the given fields replaced by the non-null parameter values. @override @JsonKey(includeFromJson: false, includeToJson: false) @pragma('vm:prefer-inline') -_$LegacyLoginViewStateCopyWith<_LegacyLoginViewState> get copyWith => __$LegacyLoginViewStateCopyWithImpl<_LegacyLoginViewState>(this, _$identity); +_$LoginStateCopyWith<_LoginState> get copyWith => __$LoginStateCopyWithImpl<_LoginState>(this, _$identity); @override bool operator ==(Object other) { - return identical(this, other) || (other.runtimeType == runtimeType&&other is _LegacyLoginViewState&&(identical(other.email, email) || other.email == email)&&(identical(other.password, password) || other.password == password)&&(identical(other.passwordVisible, passwordVisible) || other.passwordVisible == passwordVisible)&&(identical(other.emailError, emailError) || other.emailError == emailError)&&(identical(other.passwordError, passwordError) || other.passwordError == passwordError)&&(identical(other.errorEvent, errorEvent) || other.errorEvent == errorEvent)&&(identical(other.showErrors, showErrors) || other.showErrors == showErrors)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&const DeepCollectionEquality().equals(other._availableMethods, _availableMethods)&&(identical(other.token, token) || other.token == token)&&(identical(other.twoFARequested, twoFARequested) || other.twoFARequested == twoFARequested)&&(identical(other.code, code) || other.code == code)&&(identical(other.codeError, codeError) || other.codeError == codeError)&&(identical(other.resendCooldown, resendCooldown) || other.resendCooldown == resendCooldown)&&(identical(other.twoFAVerified, twoFAVerified) || other.twoFAVerified == twoFAVerified)&&(identical(other.hasDevices, hasDevices) || other.hasDevices == hasDevices)); + return identical(this, other) || (other.runtimeType == runtimeType&&other is _LoginState&&(identical(other.email, email) || other.email == email)&&(identical(other.password, password) || other.password == password)&&(identical(other.passwordVisible, passwordVisible) || other.passwordVisible == passwordVisible)&&(identical(other.emailError, emailError) || other.emailError == emailError)&&(identical(other.passwordError, passwordError) || other.passwordError == passwordError)&&(identical(other.errorEvent, errorEvent) || other.errorEvent == errorEvent)&&(identical(other.showErrors, showErrors) || other.showErrors == showErrors)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&const DeepCollectionEquality().equals(other._availableMethods, _availableMethods)&&(identical(other.token, token) || other.token == token)&&(identical(other.twoFARequested, twoFARequested) || other.twoFARequested == twoFARequested)&&(identical(other.code, code) || other.code == code)&&(identical(other.codeError, codeError) || other.codeError == codeError)&&(identical(other.resendCooldown, resendCooldown) || other.resendCooldown == resendCooldown)&&(identical(other.twoFAVerified, twoFAVerified) || other.twoFAVerified == twoFAVerified)&&(identical(other.hasDevices, hasDevices) || other.hasDevices == hasDevices)); } @@ -266,15 +266,15 @@ int get hashCode => Object.hash(runtimeType,email,password,passwordVisible,email @override String toString() { - return 'LegacyLoginViewState(email: $email, password: $password, passwordVisible: $passwordVisible, emailError: $emailError, passwordError: $passwordError, errorEvent: $errorEvent, showErrors: $showErrors, isLoading: $isLoading, availableMethods: $availableMethods, token: $token, twoFARequested: $twoFARequested, code: $code, codeError: $codeError, resendCooldown: $resendCooldown, twoFAVerified: $twoFAVerified, hasDevices: $hasDevices)'; + return 'LoginState(email: $email, password: $password, passwordVisible: $passwordVisible, emailError: $emailError, passwordError: $passwordError, errorEvent: $errorEvent, showErrors: $showErrors, isLoading: $isLoading, availableMethods: $availableMethods, token: $token, twoFARequested: $twoFARequested, code: $code, codeError: $codeError, resendCooldown: $resendCooldown, twoFAVerified: $twoFAVerified, hasDevices: $hasDevices)'; } } /// @nodoc -abstract mixin class _$LegacyLoginViewStateCopyWith<$Res> implements $LegacyLoginViewStateCopyWith<$Res> { - factory _$LegacyLoginViewStateCopyWith(_LegacyLoginViewState value, $Res Function(_LegacyLoginViewState) _then) = __$LegacyLoginViewStateCopyWithImpl; +abstract mixin class _$LoginStateCopyWith<$Res> implements $LoginStateCopyWith<$Res> { + factory _$LoginStateCopyWith(_LoginState value, $Res Function(_LoginState) _then) = __$LoginStateCopyWithImpl; @override @useResult $Res call({ String email, String password, bool passwordVisible, String emailError, String passwordError, LegacyAuthErrorEvent? errorEvent, bool showErrors, bool isLoading, List availableMethods, String token, bool twoFARequested, String code, String codeError, int resendCooldown, bool twoFAVerified, bool hasDevices @@ -285,17 +285,17 @@ $Res call({ } /// @nodoc -class __$LegacyLoginViewStateCopyWithImpl<$Res> - implements _$LegacyLoginViewStateCopyWith<$Res> { - __$LegacyLoginViewStateCopyWithImpl(this._self, this._then); +class __$LoginStateCopyWithImpl<$Res> + implements _$LoginStateCopyWith<$Res> { + __$LoginStateCopyWithImpl(this._self, this._then); - final _LegacyLoginViewState _self; - final $Res Function(_LegacyLoginViewState) _then; + final _LoginState _self; + final $Res Function(_LoginState) _then; -/// Create a copy of LegacyLoginViewState +/// Create a copy of LoginState /// with the given fields replaced by the non-null parameter values. @override @pragma('vm:prefer-inline') $Res call({Object? email = null,Object? password = null,Object? passwordVisible = null,Object? emailError = null,Object? passwordError = null,Object? errorEvent = freezed,Object? showErrors = null,Object? isLoading = null,Object? availableMethods = null,Object? token = null,Object? twoFARequested = null,Object? code = null,Object? codeError = null,Object? resendCooldown = null,Object? twoFAVerified = null,Object? hasDevices = null,}) { - return _then(_LegacyLoginViewState( + return _then(_LoginState( email: null == email ? _self.email : email // ignore: cast_nullable_to_non_nullable as String,password: null == password ? _self.password : password // ignore: cast_nullable_to_non_nullable as String,passwordVisible: null == passwordVisible ? _self.passwordVisible : passwordVisible // ignore: cast_nullable_to_non_nullable diff --git a/modules/legacy/modules/legacy_auth/lib/src/features/login/presentation/widgets/two_factor_sheet_launcher.dart b/modules/legacy/modules/legacy_auth/lib/src/features/login/presentation/widgets/two_factor_sheet_launcher.dart index 6ea1d68a..ce88f6ec 100644 --- a/modules/legacy/modules/legacy_auth/lib/src/features/login/presentation/widgets/two_factor_sheet_launcher.dart +++ b/modules/legacy/modules/legacy_auth/lib/src/features/login/presentation/widgets/two_factor_sheet_launcher.dart @@ -1,10 +1,10 @@ -import 'package:legacy_auth/src/features/login/domain/entities/legacy_auth_error_event.dart'; -import 'package:legacy_auth/src/features/login/presentation/state/login_view_model.dart'; -import 'package:legacy_auth/src/features/login/presentation/widgets/two_factor_bottom_sheet.dart'; -import 'package:design_system/design_system.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:legacy_auth/src/features/login/domain/entities/legacy_auth_error_event.dart'; +import 'package:legacy_auth/src/features/login/presentation/providers/login_controller.dart'; +import 'package:legacy_auth/src/features/login/presentation/widgets/two_factor_bottom_sheet.dart'; import 'package:sf_localizations/sf_localizations.dart'; +import 'package:sf_shared/sf_shared.dart'; String _twoFactorErrorI18nKey(LegacyAuthErrorEvent event) { return switch (event) { @@ -28,43 +28,38 @@ void showTwoFactorSheet(BuildContext context) { builder: (sheetContext) { return Consumer( builder: (sheetContext, ref, _) { - final vm = ref.read(legacyLoginViewModelProvider.notifier); + final notifier = ref.read(loginControllerProvider.notifier); - final otpErrorKey = ref.watch( - legacyLoginViewModelProvider.select((s) => s.codeError), - ); - final isLoading = ref.watch( - legacyLoginViewModelProvider.select((s) => s.isLoading), - ); - final code = ref.watch( - legacyLoginViewModelProvider.select((s) => s.code), - ); - final resendCooldown = ref.watch( - legacyLoginViewModelProvider.select((s) => s.resendCooldown), - ); + final otpErrorKey = ref + .watch(loginControllerProvider.select((s) => s.codeError)); + final isLoading = ref + .watch(loginControllerProvider.select((s) => s.isLoading)); + final code = + ref.watch(loginControllerProvider.select((s) => s.code)); + final resendCooldown = ref + .watch(loginControllerProvider.select((s) => s.resendCooldown)); final otpErrorText = otpErrorKey.isEmpty ? '' : sheetContext.translate(otpErrorKey); ref.listen( - legacyLoginViewModelProvider.select( + loginControllerProvider.select( (s) => (errorEvent: s.errorEvent, verified: s.twoFAVerified), ), - (previous, next) { + (previous, next) async { final errorEvent = next.errorEvent; if (errorEvent != null && errorEvent != previous?.errorEvent) { - showTopSnackbar( + await showErrorDialog( sheetContext, - message: sheetContext.translate( - _twoFactorErrorI18nKey(errorEvent), - ), - type: MessageType.error, + _twoFactorErrorI18nKey(errorEvent), ); - ref.read(legacyLoginViewModelProvider.notifier).clearErrorEvent(); + ref.read(loginControllerProvider.notifier).clearErrorEvent(); } if (next.verified) { - Navigator.of(sheetContext).pop(); + if (sheetContext.mounted) { + Navigator.of(sheetContext).pop(); + } } }, ); @@ -83,14 +78,14 @@ void showTwoFactorSheet(BuildContext context) { canResend: canResend, otpCode: code, otpErrorText: otpErrorText, - onChanged: vm.setCode, + onChanged: notifier.setCode, onVerify: () async { FocusManager.instance.primaryFocus?.unfocus(); - await vm.submitTwoFACode(); + await notifier.submitTwoFACode(); }, - onResend: () => vm.resendCode(), + onResend: () => notifier.resendCode(), onClose: () { - vm.dismissTwoFA(); + notifier.dismissTwoFA(); Navigator.of(sheetContext).pop(); }, );