diff --git a/flutter-ory-network/lib/blocs/auth/auth_bloc.dart b/flutter-ory-network/lib/blocs/auth/auth_bloc.dart index 6da7be0..2fcdd21 100644 --- a/flutter-ory-network/lib/blocs/auth/auth_bloc.dart +++ b/flutter-ory-network/lib/blocs/auth/auth_bloc.dart @@ -18,11 +18,11 @@ class AuthBloc extends Bloc { AuthBloc({required this.repository}) : super(const AuthState(status: AuthStatus.uninitialized)) { on(_onGetCurrentSessionInformation); - on(_changeAuthStatus); - on(_logOut); + on(_onChangeAuthStatus); + on(_onLogOut); } - _changeAuthStatus(ChangeAuthStatus event, Emitter emit) { + _onChangeAuthStatus(ChangeAuthStatus event, Emitter emit) { emit(state.copyWith(status: event.status, isLoading: false)); } @@ -43,6 +43,9 @@ class AuthBloc extends Bloc { status: AuthStatus.unauthenticated, session: null, isLoading: false)); + } else if (e case TwoFactorAuthRequiredException _) { + emit(state.copyWith( + isLoading: false, session: null, status: AuthStatus.aal2Requested)); } else if (e case UnknownException _) { emit(state.copyWith(isLoading: false, errorMessage: e.message)); } else { @@ -51,7 +54,7 @@ class AuthBloc extends Bloc { } } - Future _logOut(LogOut event, Emitter emit) async { + Future _onLogOut(LogOut event, Emitter emit) async { try { emit(state.copyWith(isLoading: true, errorMessage: null)); diff --git a/flutter-ory-network/lib/blocs/auth/auth_bloc.freezed.dart b/flutter-ory-network/lib/blocs/auth/auth_bloc.freezed.dart index c9fb075..02acfd8 100644 --- a/flutter-ory-network/lib/blocs/auth/auth_bloc.freezed.dart +++ b/flutter-ory-network/lib/blocs/auth/auth_bloc.freezed.dart @@ -1,6 +1,3 @@ -// Copyright © 2023 Ory Corp -// SPDX-License-Identifier: Apache-2.0 - // coverage:ignore-file // GENERATED CODE - DO NOT MODIFY BY HAND // ignore_for_file: type=lint diff --git a/flutter-ory-network/lib/blocs/login/login_bloc.dart b/flutter-ory-network/lib/blocs/login/login_bloc.dart index a3b3655..fcdcd63 100644 --- a/flutter-ory-network/lib/blocs/login/login_bloc.dart +++ b/flutter-ory-network/lib/blocs/login/login_bloc.dart @@ -4,9 +4,8 @@ import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; -import 'package:collection/collection.dart'; +import 'package:ory_client/ory_client.dart'; -import '../../entities/formfield.dart'; import '../../repositories/auth.dart'; import '../../services/exceptions.dart'; import '../auth/auth_bloc.dart'; @@ -21,88 +20,71 @@ class LoginBloc extends Bloc { LoginBloc({required this.authBloc, required this.repository}) : super(LoginState()) { on(_onCreateLoginFlow); - on(_onChangeEmail); - on(_onChangePassword); - on(_onChangePasswordVisibility); - on(_onLoginWithEmailAndPassword); + on(_onGetLoginFlow); + on(_onChangeNodeValue); + on(_onUpdateLoginFlow); } Future _onCreateLoginFlow( CreateLoginFlow event, Emitter emit) async { try { - emit(state.copyWith(isLoading: true, errorMessage: null)); - final flowId = await repository.createLoginFlow(); - emit(state.copyWith(flowId: flowId, isLoading: false)); + emit(state.copyWith(isLoading: true, message: null)); + final loginFlow = await repository.createLoginFlow(aal: event.aal); + if (event.aal == 'aal2') { + authBloc.add(ChangeAuthStatus(status: AuthStatus.aal2Requested)); + } + emit(state.copyWith(loginFlow: loginFlow, isLoading: false)); } on CustomException catch (e) { if (e case UnknownException _) { - emit(state.copyWith(isLoading: false, errorMessage: e.message)); + emit(state.copyWith(isLoading: false, message: e.message)); } else { emit(state.copyWith(isLoading: false)); } } } - _onChangeEmail(ChangeEmail event, Emitter emit) { - // remove email and general error when changing email - emit(state.copyWith - .email(value: event.value, errorMessage: null) - .copyWith(errorMessage: null)); - } - - _onChangePassword(ChangePassword event, Emitter emit) { - // remove password and general error when changing email - emit(state.copyWith - .password(value: event.value, errorMessage: null) - .copyWith(errorMessage: null)); + Future _onGetLoginFlow( + GetLoginFlow event, Emitter emit) async { + try { + emit(state.copyWith(isLoading: true, message: null)); + final loginFlow = await repository.getLoginFlow(flowId: event.flowId); + emit(state.copyWith(loginFlow: loginFlow, isLoading: false)); + } on CustomException catch (e) { + if (e case UnknownException _) { + emit(state.copyWith(isLoading: false, message: e.message)); + } else { + emit(state.copyWith(isLoading: false)); + } + } } - _onChangePasswordVisibility( - ChangePasswordVisibility event, Emitter emit) { - emit(state.copyWith(isPasswordHidden: event.value)); + _onChangeNodeValue(ChangeNodeValue event, Emitter emit) { + if (state.loginFlow != null) { + final newLoginState = repository.changeLoginNodeValue( + settings: state.loginFlow!, name: event.name, value: event.value); + emit(state.copyWith(loginFlow: newLoginState, message: null)); + } } - Future _onLoginWithEmailAndPassword( - LoginWithEmailAndPassword event, Emitter emit) async { + _onUpdateLoginFlow(UpdateLoginFlow event, Emitter emit) async { try { - // remove error messages when performing login - emit(state - .copyWith(isLoading: true, errorMessage: null) - .copyWith - .email(errorMessage: null) - .copyWith - .password(errorMessage: null)); - - await repository.loginWithEmailAndPassword( - flowId: event.flowId, email: event.email, password: event.password); - + emit(state.copyWith(isLoading: true, message: null)); + await repository.updateLoginFlow( + flowId: state.loginFlow!.id, + group: event.group, + name: event.name, + value: event.value, + nodes: state.loginFlow!.ui.nodes.toList()); authBloc.add(ChangeAuthStatus(status: AuthStatus.authenticated)); } on CustomException catch (e) { - if (e case BadRequestException _) { - final emailMessage = e.messages - ?.firstWhereOrNull((element) => element.attr == 'identifier'); - final passwordMessage = e.messages - ?.firstWhereOrNull((element) => element.attr == 'password'); - final generalMessage = e.messages - ?.firstWhereOrNull((element) => element.attr == 'general'); - - // update state to new one with errors - emit(state - .copyWith(isLoading: false, errorMessage: generalMessage?.text) - .copyWith - .email(errorMessage: emailMessage?.text) - .copyWith - .password(errorMessage: passwordMessage?.text)); + if (e case BadRequestException _) { + emit(state.copyWith(loginFlow: e.flow, isLoading: false)); } else if (e case FlowExpiredException _) { - // use new flow id, reset fields and show error - emit(state - .copyWith( - flowId: e.flowId, errorMessage: e.message, isLoading: false) - .copyWith - .email(value: '') - .copyWith - .password(value: '')); + add(GetLoginFlow(flowId: e.flowId)); + } else if (e case TwoFactorAuthRequiredException _) { + add(CreateLoginFlow(aal: 'aal2')); } else if (e case UnknownException _) { - emit(state.copyWith(isLoading: false, errorMessage: e.message)); + emit(state.copyWith(isLoading: false, message: e.message)); } else { emit(state.copyWith(isLoading: false)); } diff --git a/flutter-ory-network/lib/blocs/login/login_bloc.freezed.dart b/flutter-ory-network/lib/blocs/login/login_bloc.freezed.dart index 8efa7a9..d09b7b6 100644 --- a/flutter-ory-network/lib/blocs/login/login_bloc.freezed.dart +++ b/flutter-ory-network/lib/blocs/login/login_bloc.freezed.dart @@ -1,6 +1,3 @@ -// Copyright © 2023 Ory Corp -// SPDX-License-Identifier: Apache-2.0 - // coverage:ignore-file // GENERATED CODE - DO NOT MODIFY BY HAND // ignore_for_file: type=lint @@ -19,12 +16,9 @@ final _privateConstructorUsedError = UnsupportedError( /// @nodoc mixin _$LoginState { - String? get flowId => throw _privateConstructorUsedError; - FormField get email => throw _privateConstructorUsedError; - FormField get password => throw _privateConstructorUsedError; - bool get isPasswordHidden => throw _privateConstructorUsedError; + LoginFlow? get loginFlow => throw _privateConstructorUsedError; bool get isLoading => throw _privateConstructorUsedError; - String? get errorMessage => throw _privateConstructorUsedError; + String? get message => throw _privateConstructorUsedError; @JsonKey(ignore: true) $LoginStateCopyWith get copyWith => @@ -37,16 +31,7 @@ abstract class $LoginStateCopyWith<$Res> { LoginState value, $Res Function(LoginState) then) = _$LoginStateCopyWithImpl<$Res, LoginState>; @useResult - $Res call( - {String? flowId, - FormField email, - FormField password, - bool isPasswordHidden, - bool isLoading, - String? errorMessage}); - - $FormFieldCopyWith get email; - $FormFieldCopyWith get password; + $Res call({LoginFlow? loginFlow, bool isLoading, String? message}); } /// @nodoc @@ -62,56 +47,25 @@ class _$LoginStateCopyWithImpl<$Res, $Val extends LoginState> @pragma('vm:prefer-inline') @override $Res call({ - Object? flowId = freezed, - Object? email = null, - Object? password = null, - Object? isPasswordHidden = null, + Object? loginFlow = freezed, Object? isLoading = null, - Object? errorMessage = freezed, + Object? message = freezed, }) { return _then(_value.copyWith( - flowId: freezed == flowId - ? _value.flowId - : flowId // ignore: cast_nullable_to_non_nullable - as String?, - email: null == email - ? _value.email - : email // ignore: cast_nullable_to_non_nullable - as FormField, - password: null == password - ? _value.password - : password // ignore: cast_nullable_to_non_nullable - as FormField, - isPasswordHidden: null == isPasswordHidden - ? _value.isPasswordHidden - : isPasswordHidden // ignore: cast_nullable_to_non_nullable - as bool, + loginFlow: freezed == loginFlow + ? _value.loginFlow + : loginFlow // ignore: cast_nullable_to_non_nullable + as LoginFlow?, isLoading: null == isLoading ? _value.isLoading : isLoading // ignore: cast_nullable_to_non_nullable as bool, - errorMessage: freezed == errorMessage - ? _value.errorMessage - : errorMessage // ignore: cast_nullable_to_non_nullable + message: freezed == message + ? _value.message + : message // ignore: cast_nullable_to_non_nullable as String?, ) as $Val); } - - @override - @pragma('vm:prefer-inline') - $FormFieldCopyWith get email { - return $FormFieldCopyWith(_value.email, (value) { - return _then(_value.copyWith(email: value) as $Val); - }); - } - - @override - @pragma('vm:prefer-inline') - $FormFieldCopyWith get password { - return $FormFieldCopyWith(_value.password, (value) { - return _then(_value.copyWith(password: value) as $Val); - }); - } } /// @nodoc @@ -122,18 +76,7 @@ abstract class _$$_LoginStateCopyWith<$Res> __$$_LoginStateCopyWithImpl<$Res>; @override @useResult - $Res call( - {String? flowId, - FormField email, - FormField password, - bool isPasswordHidden, - bool isLoading, - String? errorMessage}); - - @override - $FormFieldCopyWith get email; - @override - $FormFieldCopyWith get password; + $Res call({LoginFlow? loginFlow, bool isLoading, String? message}); } /// @nodoc @@ -147,37 +90,22 @@ class __$$_LoginStateCopyWithImpl<$Res> @pragma('vm:prefer-inline') @override $Res call({ - Object? flowId = freezed, - Object? email = null, - Object? password = null, - Object? isPasswordHidden = null, + Object? loginFlow = freezed, Object? isLoading = null, - Object? errorMessage = freezed, + Object? message = freezed, }) { return _then(_$_LoginState( - flowId: freezed == flowId - ? _value.flowId - : flowId // ignore: cast_nullable_to_non_nullable - as String?, - email: null == email - ? _value.email - : email // ignore: cast_nullable_to_non_nullable - as FormField, - password: null == password - ? _value.password - : password // ignore: cast_nullable_to_non_nullable - as FormField, - isPasswordHidden: null == isPasswordHidden - ? _value.isPasswordHidden - : isPasswordHidden // ignore: cast_nullable_to_non_nullable - as bool, + loginFlow: freezed == loginFlow + ? _value.loginFlow + : loginFlow // ignore: cast_nullable_to_non_nullable + as LoginFlow?, isLoading: null == isLoading ? _value.isLoading : isLoading // ignore: cast_nullable_to_non_nullable as bool, - errorMessage: freezed == errorMessage - ? _value.errorMessage - : errorMessage // ignore: cast_nullable_to_non_nullable + message: freezed == message + ? _value.message + : message // ignore: cast_nullable_to_non_nullable as String?, )); } @@ -186,34 +114,19 @@ class __$$_LoginStateCopyWithImpl<$Res> /// @nodoc class _$_LoginState implements _LoginState { - const _$_LoginState( - {this.flowId, - this.email = const FormField(value: ''), - this.password = const FormField(value: ''), - this.isPasswordHidden = true, - this.isLoading = false, - this.errorMessage}); + const _$_LoginState({this.loginFlow, this.isLoading = false, this.message}); @override - final String? flowId; - @override - @JsonKey() - final FormField email; - @override - @JsonKey() - final FormField password; - @override - @JsonKey() - final bool isPasswordHidden; + final LoginFlow? loginFlow; @override @JsonKey() final bool isLoading; @override - final String? errorMessage; + final String? message; @override String toString() { - return 'LoginState(flowId: $flowId, email: $email, password: $password, isPasswordHidden: $isPasswordHidden, isLoading: $isLoading, errorMessage: $errorMessage)'; + return 'LoginState(loginFlow: $loginFlow, isLoading: $isLoading, message: $message)'; } @override @@ -221,21 +134,15 @@ class _$_LoginState implements _LoginState { return identical(this, other) || (other.runtimeType == runtimeType && other is _$_LoginState && - (identical(other.flowId, flowId) || other.flowId == flowId) && - (identical(other.email, email) || other.email == email) && - (identical(other.password, password) || - other.password == password) && - (identical(other.isPasswordHidden, isPasswordHidden) || - other.isPasswordHidden == isPasswordHidden) && + (identical(other.loginFlow, loginFlow) || + other.loginFlow == loginFlow) && (identical(other.isLoading, isLoading) || other.isLoading == isLoading) && - (identical(other.errorMessage, errorMessage) || - other.errorMessage == errorMessage)); + (identical(other.message, message) || other.message == message)); } @override - int get hashCode => Object.hash(runtimeType, flowId, email, password, - isPasswordHidden, isLoading, errorMessage); + int get hashCode => Object.hash(runtimeType, loginFlow, isLoading, message); @JsonKey(ignore: true) @override @@ -246,25 +153,16 @@ class _$_LoginState implements _LoginState { abstract class _LoginState implements LoginState { const factory _LoginState( - {final String? flowId, - final FormField email, - final FormField password, - final bool isPasswordHidden, + {final LoginFlow? loginFlow, final bool isLoading, - final String? errorMessage}) = _$_LoginState; + final String? message}) = _$_LoginState; @override - String? get flowId; - @override - FormField get email; - @override - FormField get password; - @override - bool get isPasswordHidden; + LoginFlow? get loginFlow; @override bool get isLoading; @override - String? get errorMessage; + String? get message; @override @JsonKey(ignore: true) _$$_LoginStateCopyWith<_$_LoginState> get copyWith => diff --git a/flutter-ory-network/lib/blocs/login/login_event.dart b/flutter-ory-network/lib/blocs/login/login_event.dart index b80c7f3..b048895 100644 --- a/flutter-ory-network/lib/blocs/login/login_event.dart +++ b/flutter-ory-network/lib/blocs/login/login_event.dart @@ -6,48 +6,41 @@ part of 'login_bloc.dart'; @immutable sealed class LoginEvent extends Equatable { @override - List get props => []; + List get props => []; } -//create login flow -final class CreateLoginFlow extends LoginEvent {} - -final class ChangeEmail extends LoginEvent { - final String value; - - ChangeEmail({required this.value}); +final class CreateLoginFlow extends LoginEvent { + final String aal; + CreateLoginFlow({required this.aal}); @override - List get props => [value]; + List get props => [aal]; } -final class ChangePassword extends LoginEvent { +class ChangeNodeValue extends LoginEvent { final String value; + final String name; - ChangePassword({required this.value}); - + ChangeNodeValue({required this.value, required this.name}); @override - List get props => [value]; + List get props => [value, name]; } -final class ChangePasswordVisibility extends LoginEvent { - final bool value; - - ChangePasswordVisibility({required this.value}); +class GetLoginFlow extends LoginEvent { + final String flowId; + GetLoginFlow({required this.flowId}); @override - List get props => [value]; + List get props => [flowId]; } -//log in -final class LoginWithEmailAndPassword extends LoginEvent { - final String flowId; - final String email; - final String password; - - LoginWithEmailAndPassword( - {required this.flowId, required this.email, required this.password}); +class UpdateLoginFlow extends LoginEvent { + final UiNodeGroupEnum group; + final String name; + final String value; + UpdateLoginFlow( + {required this.group, required this.name, required this.value}); @override - List get props => [flowId, email, password]; + List get props => [group, name, value]; } diff --git a/flutter-ory-network/lib/blocs/login/login_state.dart b/flutter-ory-network/lib/blocs/login/login_state.dart index 3c627c0..f28cce9 100644 --- a/flutter-ory-network/lib/blocs/login/login_state.dart +++ b/flutter-ory-network/lib/blocs/login/login_state.dart @@ -6,10 +6,7 @@ part of 'login_bloc.dart'; @freezed sealed class LoginState with _$LoginState { const factory LoginState( - {String? flowId, - @Default(FormField(value: '')) FormField email, - @Default(FormField(value: '')) FormField password, - @Default(true) bool isPasswordHidden, + {LoginFlow? loginFlow, @Default(false) bool isLoading, - String? errorMessage}) = _LoginState; + String? message}) = _LoginState; } diff --git a/flutter-ory-network/lib/blocs/registration/registration_bloc.dart b/flutter-ory-network/lib/blocs/registration/registration_bloc.dart index 84acbb8..d4f4224 100644 --- a/flutter-ory-network/lib/blocs/registration/registration_bloc.dart +++ b/flutter-ory-network/lib/blocs/registration/registration_bloc.dart @@ -4,9 +4,8 @@ import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; -import 'package:collection/collection.dart'; +import 'package:ory_client/ory_client.dart'; -import '../../entities/formfield.dart'; import '../../repositories/auth.dart'; import '../../services/exceptions.dart'; import '../auth/auth_bloc.dart'; @@ -21,90 +20,72 @@ class RegistrationBloc extends Bloc { RegistrationBloc({required this.authBloc, required this.repository}) : super(RegistrationState()) { on(_onCreateRegistrationFlow); - on(_onChangeEmail); - on(_onChangePassword); - on(_onChangePasswordVisibility); - on(_onRegisterWithEmailAndPassword); + on(_onGetRegistrationFlow); + on(_onChangeNodeValue); + on(_onUpdateRegistrationFlow); } Future _onCreateRegistrationFlow( CreateRegistrationFlow event, Emitter emit) async { try { - emit(state.copyWith(isLoading: true, errorMessage: null)); - final flowId = await repository.createRegistrationFlow(); - emit(state.copyWith(flowId: flowId, isLoading: false)); + emit(state.copyWith(isLoading: true, message: null)); + final flow = await repository.createRegistrationFlow(); + emit(state.copyWith(registrationFlow: flow, isLoading: false)); } on CustomException catch (e) { if (e case UnknownException _) { - emit(state.copyWith(isLoading: false, errorMessage: e.message)); + emit(state.copyWith(isLoading: false, message: e.message)); } else { emit(state.copyWith(isLoading: false)); } } } - _onChangeEmail(ChangeEmail event, Emitter emit) { - // remove email and general error when changing email - emit(state.copyWith - .email(value: event.value, errorMessage: null) - .copyWith(errorMessage: null)); - } - - _onChangePassword(ChangePassword event, Emitter emit) { - // remove password and general error when changing email - emit(state.copyWith - .password(value: event.value, errorMessage: null) - .copyWith(errorMessage: null)); + Future _onGetRegistrationFlow( + GetRegistrationFlow event, Emitter emit) async { + try { + emit(state.copyWith(isLoading: true, message: null)); + final flow = await repository.getRegistrationFlow(flowId: event.flowId); + emit(state.copyWith(registrationFlow: flow, isLoading: false)); + } on CustomException catch (e) { + if (e case UnknownException _) { + emit(state.copyWith(isLoading: false, message: e.message)); + } else { + emit(state.copyWith(isLoading: false)); + } + } } - _onChangePasswordVisibility( - ChangePasswordVisibility event, Emitter emit) { - emit(state.copyWith(isPasswordHidden: event.value)); + _onChangeNodeValue(ChangeNodeValue event, Emitter emit) { + if (state.registrationFlow != null) { + final newRegistrationState = repository.changeRegistrationNodeValue( + settings: state.registrationFlow!, + name: event.name, + value: event.value); + emit(state.copyWith( + registrationFlow: newRegistrationState, message: null)); + } } - Future _onRegisterWithEmailAndPassword( - RegisterWithEmailAndPassword event, - Emitter emit) async { + Future _onUpdateRegistrationFlow( + UpdateRegistrationFlow event, Emitter emit) async { try { - // remove error messages when performing registration - emit(state - .copyWith(isLoading: true, errorMessage: null) - .copyWith - .email(errorMessage: null) - .copyWith - .password(errorMessage: null)); - - await repository.registerWithEmailAndPassword( - flowId: event.flowId, email: event.email, password: event.password); - - authBloc.add(ChangeAuthStatus(status: AuthStatus.authenticated)); + if (state.registrationFlow != null) { + emit(state.copyWith(isLoading: true, message: null)); + await repository.updateRegistrationFlow( + flowId: state.registrationFlow!.id, + group: event.group, + name: event.name, + value: event.value, + nodes: state.registrationFlow!.ui.nodes.toList()); + authBloc.add(ChangeAuthStatus(status: AuthStatus.authenticated)); + } } on CustomException catch (e) { - if (e case BadRequestException _) { - // get credential errors - final emailMessage = e.messages - ?.firstWhereOrNull((element) => element.attr == 'traits.email'); - final passwordMessage = e.messages - ?.firstWhereOrNull((element) => element.attr == 'password'); - final generalMessage = e.messages - ?.firstWhereOrNull((element) => element.attr == 'general'); - - // update state to new one with errors - emit(state - .copyWith(isLoading: false, errorMessage: generalMessage?.text) - .copyWith - .email(errorMessage: emailMessage?.text) - .copyWith - .password(errorMessage: passwordMessage?.text)); + if (e case BadRequestException _) { + emit(state.copyWith(registrationFlow: e.flow, isLoading: false)); } else if (e case FlowExpiredException _) { - // use new flow id, reset fields and show error - emit(state - .copyWith( - flowId: e.flowId, errorMessage: e.message, isLoading: false) - .copyWith - .email(value: '') - .copyWith - .password(value: '')); + add(GetRegistrationFlow(flowId: e.flowId)); } else if (e case UnknownException _) { - emit(state.copyWith(isLoading: false, errorMessage: e.message)); + emit(state.copyWith(isLoading: false, message: e.message)); } else { emit(state.copyWith(isLoading: false)); } diff --git a/flutter-ory-network/lib/blocs/registration/registration_bloc.freezed.dart b/flutter-ory-network/lib/blocs/registration/registration_bloc.freezed.dart index c5f63b5..d1f90d5 100644 --- a/flutter-ory-network/lib/blocs/registration/registration_bloc.freezed.dart +++ b/flutter-ory-network/lib/blocs/registration/registration_bloc.freezed.dart @@ -1,6 +1,3 @@ -// Copyright © 2023 Ory Corp -// SPDX-License-Identifier: Apache-2.0 - // coverage:ignore-file // GENERATED CODE - DO NOT MODIFY BY HAND // ignore_for_file: type=lint @@ -19,12 +16,9 @@ final _privateConstructorUsedError = UnsupportedError( /// @nodoc mixin _$RegistrationState { - String? get flowId => throw _privateConstructorUsedError; - FormField get email => throw _privateConstructorUsedError; - FormField get password => throw _privateConstructorUsedError; - bool get isPasswordHidden => throw _privateConstructorUsedError; + RegistrationFlow? get registrationFlow => throw _privateConstructorUsedError; bool get isLoading => throw _privateConstructorUsedError; - String? get errorMessage => throw _privateConstructorUsedError; + String? get message => throw _privateConstructorUsedError; @JsonKey(ignore: true) $RegistrationStateCopyWith get copyWith => @@ -38,15 +32,7 @@ abstract class $RegistrationStateCopyWith<$Res> { _$RegistrationStateCopyWithImpl<$Res, RegistrationState>; @useResult $Res call( - {String? flowId, - FormField email, - FormField password, - bool isPasswordHidden, - bool isLoading, - String? errorMessage}); - - $FormFieldCopyWith get email; - $FormFieldCopyWith get password; + {RegistrationFlow? registrationFlow, bool isLoading, String? message}); } /// @nodoc @@ -62,56 +48,25 @@ class _$RegistrationStateCopyWithImpl<$Res, $Val extends RegistrationState> @pragma('vm:prefer-inline') @override $Res call({ - Object? flowId = freezed, - Object? email = null, - Object? password = null, - Object? isPasswordHidden = null, + Object? registrationFlow = freezed, Object? isLoading = null, - Object? errorMessage = freezed, + Object? message = freezed, }) { return _then(_value.copyWith( - flowId: freezed == flowId - ? _value.flowId - : flowId // ignore: cast_nullable_to_non_nullable - as String?, - email: null == email - ? _value.email - : email // ignore: cast_nullable_to_non_nullable - as FormField, - password: null == password - ? _value.password - : password // ignore: cast_nullable_to_non_nullable - as FormField, - isPasswordHidden: null == isPasswordHidden - ? _value.isPasswordHidden - : isPasswordHidden // ignore: cast_nullable_to_non_nullable - as bool, + registrationFlow: freezed == registrationFlow + ? _value.registrationFlow + : registrationFlow // ignore: cast_nullable_to_non_nullable + as RegistrationFlow?, isLoading: null == isLoading ? _value.isLoading : isLoading // ignore: cast_nullable_to_non_nullable as bool, - errorMessage: freezed == errorMessage - ? _value.errorMessage - : errorMessage // ignore: cast_nullable_to_non_nullable + message: freezed == message + ? _value.message + : message // ignore: cast_nullable_to_non_nullable as String?, ) as $Val); } - - @override - @pragma('vm:prefer-inline') - $FormFieldCopyWith get email { - return $FormFieldCopyWith(_value.email, (value) { - return _then(_value.copyWith(email: value) as $Val); - }); - } - - @override - @pragma('vm:prefer-inline') - $FormFieldCopyWith get password { - return $FormFieldCopyWith(_value.password, (value) { - return _then(_value.copyWith(password: value) as $Val); - }); - } } /// @nodoc @@ -123,17 +78,7 @@ abstract class _$$_RegistrationStateCopyWith<$Res> @override @useResult $Res call( - {String? flowId, - FormField email, - FormField password, - bool isPasswordHidden, - bool isLoading, - String? errorMessage}); - - @override - $FormFieldCopyWith get email; - @override - $FormFieldCopyWith get password; + {RegistrationFlow? registrationFlow, bool isLoading, String? message}); } /// @nodoc @@ -147,37 +92,22 @@ class __$$_RegistrationStateCopyWithImpl<$Res> @pragma('vm:prefer-inline') @override $Res call({ - Object? flowId = freezed, - Object? email = null, - Object? password = null, - Object? isPasswordHidden = null, + Object? registrationFlow = freezed, Object? isLoading = null, - Object? errorMessage = freezed, + Object? message = freezed, }) { return _then(_$_RegistrationState( - flowId: freezed == flowId - ? _value.flowId - : flowId // ignore: cast_nullable_to_non_nullable - as String?, - email: null == email - ? _value.email - : email // ignore: cast_nullable_to_non_nullable - as FormField, - password: null == password - ? _value.password - : password // ignore: cast_nullable_to_non_nullable - as FormField, - isPasswordHidden: null == isPasswordHidden - ? _value.isPasswordHidden - : isPasswordHidden // ignore: cast_nullable_to_non_nullable - as bool, + registrationFlow: freezed == registrationFlow + ? _value.registrationFlow + : registrationFlow // ignore: cast_nullable_to_non_nullable + as RegistrationFlow?, isLoading: null == isLoading ? _value.isLoading : isLoading // ignore: cast_nullable_to_non_nullable as bool, - errorMessage: freezed == errorMessage - ? _value.errorMessage - : errorMessage // ignore: cast_nullable_to_non_nullable + message: freezed == message + ? _value.message + : message // ignore: cast_nullable_to_non_nullable as String?, )); } @@ -187,33 +117,19 @@ class __$$_RegistrationStateCopyWithImpl<$Res> class _$_RegistrationState implements _RegistrationState { const _$_RegistrationState( - {this.flowId, - this.email = const FormField(value: ''), - this.password = const FormField(value: ''), - this.isPasswordHidden = true, - this.isLoading = false, - this.errorMessage}); + {this.registrationFlow, this.isLoading = false, this.message}); @override - final String? flowId; - @override - @JsonKey() - final FormField email; - @override - @JsonKey() - final FormField password; - @override - @JsonKey() - final bool isPasswordHidden; + final RegistrationFlow? registrationFlow; @override @JsonKey() final bool isLoading; @override - final String? errorMessage; + final String? message; @override String toString() { - return 'RegistrationState(flowId: $flowId, email: $email, password: $password, isPasswordHidden: $isPasswordHidden, isLoading: $isLoading, errorMessage: $errorMessage)'; + return 'RegistrationState(registrationFlow: $registrationFlow, isLoading: $isLoading, message: $message)'; } @override @@ -221,21 +137,16 @@ class _$_RegistrationState implements _RegistrationState { return identical(this, other) || (other.runtimeType == runtimeType && other is _$_RegistrationState && - (identical(other.flowId, flowId) || other.flowId == flowId) && - (identical(other.email, email) || other.email == email) && - (identical(other.password, password) || - other.password == password) && - (identical(other.isPasswordHidden, isPasswordHidden) || - other.isPasswordHidden == isPasswordHidden) && + (identical(other.registrationFlow, registrationFlow) || + other.registrationFlow == registrationFlow) && (identical(other.isLoading, isLoading) || other.isLoading == isLoading) && - (identical(other.errorMessage, errorMessage) || - other.errorMessage == errorMessage)); + (identical(other.message, message) || other.message == message)); } @override - int get hashCode => Object.hash(runtimeType, flowId, email, password, - isPasswordHidden, isLoading, errorMessage); + int get hashCode => + Object.hash(runtimeType, registrationFlow, isLoading, message); @JsonKey(ignore: true) @override @@ -247,25 +158,16 @@ class _$_RegistrationState implements _RegistrationState { abstract class _RegistrationState implements RegistrationState { const factory _RegistrationState( - {final String? flowId, - final FormField email, - final FormField password, - final bool isPasswordHidden, + {final RegistrationFlow? registrationFlow, final bool isLoading, - final String? errorMessage}) = _$_RegistrationState; + final String? message}) = _$_RegistrationState; @override - String? get flowId; - @override - FormField get email; - @override - FormField get password; - @override - bool get isPasswordHidden; + RegistrationFlow? get registrationFlow; @override bool get isLoading; @override - String? get errorMessage; + String? get message; @override @JsonKey(ignore: true) _$$_RegistrationStateCopyWith<_$_RegistrationState> get copyWith => diff --git a/flutter-ory-network/lib/blocs/registration/registration_event.dart b/flutter-ory-network/lib/blocs/registration/registration_event.dart index 60dcffe..164cac0 100644 --- a/flutter-ory-network/lib/blocs/registration/registration_event.dart +++ b/flutter-ory-network/lib/blocs/registration/registration_event.dart @@ -9,44 +9,32 @@ sealed class RegistrationEvent extends Equatable { List get props => []; } -//create registration flow final class CreateRegistrationFlow extends RegistrationEvent {} -final class ChangeEmail extends RegistrationEvent { - final String value; - - ChangeEmail({required this.value}); +final class GetRegistrationFlow extends RegistrationEvent { + final String flowId; + GetRegistrationFlow({required this.flowId}); @override - List get props => [value]; + List get props => [flowId]; } -final class ChangePassword extends RegistrationEvent { +class ChangeNodeValue extends RegistrationEvent { final String value; + final String name; - ChangePassword({required this.value}); - + ChangeNodeValue({required this.value, required this.name}); @override - List get props => [value]; + List get props => [value, name]; } -final class ChangePasswordVisibility extends RegistrationEvent { - final bool value; - - ChangePasswordVisibility({required this.value}); - - @override - List get props => [value]; -} //log in - -final class RegisterWithEmailAndPassword extends RegistrationEvent { - final String flowId; - final String email; - final String password; - - RegisterWithEmailAndPassword( - {required this.flowId, required this.email, required this.password}); +class UpdateRegistrationFlow extends RegistrationEvent { + final UiNodeGroupEnum group; + final String name; + final String value; + UpdateRegistrationFlow( + {required this.group, required this.name, required this.value}); @override - List get props => [flowId, email, password]; + List get props => [group, name, value]; } diff --git a/flutter-ory-network/lib/blocs/registration/registration_state.dart b/flutter-ory-network/lib/blocs/registration/registration_state.dart index 38eac5b..35eb520 100644 --- a/flutter-ory-network/lib/blocs/registration/registration_state.dart +++ b/flutter-ory-network/lib/blocs/registration/registration_state.dart @@ -6,10 +6,7 @@ part of 'registration_bloc.dart'; @freezed sealed class RegistrationState with _$RegistrationState { const factory RegistrationState( - {String? flowId, - @Default(FormField(value: '')) FormField email, - @Default(FormField(value: '')) FormField password, - @Default(true) bool isPasswordHidden, + {RegistrationFlow? registrationFlow, @Default(false) bool isLoading, - String? errorMessage}) = _RegistrationState; + String? message}) = _RegistrationState; } diff --git a/flutter-ory-network/lib/entities/form_node.dart b/flutter-ory-network/lib/entities/form_node.dart deleted file mode 100644 index 57e2ed7..0000000 --- a/flutter-ory-network/lib/entities/form_node.dart +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright © 2023 Ory Corp -// SPDX-License-Identifier: Apache-2.0 - -import 'package:freezed_annotation/freezed_annotation.dart'; - -import 'message.dart'; -import 'node_attribute.dart'; - -part 'form_node.freezed.dart'; -part 'form_node.g.dart'; - -@freezed -sealed class FormNode with _$FormNode { - const factory FormNode( - {required NodeAttribute attributes, - required List messages}) = _FormNode; - - factory FormNode.fromJson(Map json) => - _$FormNodeFromJson(json); -} diff --git a/flutter-ory-network/lib/entities/form_node.freezed.dart b/flutter-ory-network/lib/entities/form_node.freezed.dart deleted file mode 100644 index 96efd5e..0000000 --- a/flutter-ory-network/lib/entities/form_node.freezed.dart +++ /dev/null @@ -1,191 +0,0 @@ -// Copyright © 2023 Ory Corp -// SPDX-License-Identifier: Apache-2.0 - -// coverage:ignore-file -// GENERATED CODE - DO NOT MODIFY BY HAND -// 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 'form_node.dart'; - -// ************************************************************************** -// FreezedGenerator -// ************************************************************************** - -T _$identity(T value) => value; - -final _privateConstructorUsedError = UnsupportedError( - 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); - -FormNode _$FormNodeFromJson(Map json) { - return _FormNode.fromJson(json); -} - -/// @nodoc -mixin _$FormNode { - NodeAttribute get attributes => throw _privateConstructorUsedError; - List get messages => throw _privateConstructorUsedError; - - Map toJson() => throw _privateConstructorUsedError; - @JsonKey(ignore: true) - $FormNodeCopyWith get copyWith => - throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class $FormNodeCopyWith<$Res> { - factory $FormNodeCopyWith(FormNode value, $Res Function(FormNode) then) = - _$FormNodeCopyWithImpl<$Res, FormNode>; - @useResult - $Res call({NodeAttribute attributes, List messages}); - - $NodeAttributeCopyWith<$Res> get attributes; -} - -/// @nodoc -class _$FormNodeCopyWithImpl<$Res, $Val extends FormNode> - implements $FormNodeCopyWith<$Res> { - _$FormNodeCopyWithImpl(this._value, this._then); - - // ignore: unused_field - final $Val _value; - // ignore: unused_field - final $Res Function($Val) _then; - - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? attributes = null, - Object? messages = null, - }) { - return _then(_value.copyWith( - attributes: null == attributes - ? _value.attributes - : attributes // ignore: cast_nullable_to_non_nullable - as NodeAttribute, - messages: null == messages - ? _value.messages - : messages // ignore: cast_nullable_to_non_nullable - as List, - ) as $Val); - } - - @override - @pragma('vm:prefer-inline') - $NodeAttributeCopyWith<$Res> get attributes { - return $NodeAttributeCopyWith<$Res>(_value.attributes, (value) { - return _then(_value.copyWith(attributes: value) as $Val); - }); - } -} - -/// @nodoc -abstract class _$$_FormNodeCopyWith<$Res> implements $FormNodeCopyWith<$Res> { - factory _$$_FormNodeCopyWith( - _$_FormNode value, $Res Function(_$_FormNode) then) = - __$$_FormNodeCopyWithImpl<$Res>; - @override - @useResult - $Res call({NodeAttribute attributes, List messages}); - - @override - $NodeAttributeCopyWith<$Res> get attributes; -} - -/// @nodoc -class __$$_FormNodeCopyWithImpl<$Res> - extends _$FormNodeCopyWithImpl<$Res, _$_FormNode> - implements _$$_FormNodeCopyWith<$Res> { - __$$_FormNodeCopyWithImpl( - _$_FormNode _value, $Res Function(_$_FormNode) _then) - : super(_value, _then); - - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? attributes = null, - Object? messages = null, - }) { - return _then(_$_FormNode( - attributes: null == attributes - ? _value.attributes - : attributes // ignore: cast_nullable_to_non_nullable - as NodeAttribute, - messages: null == messages - ? _value._messages - : messages // ignore: cast_nullable_to_non_nullable - as List, - )); - } -} - -/// @nodoc -@JsonSerializable() -class _$_FormNode implements _FormNode { - const _$_FormNode( - {required this.attributes, required final List messages}) - : _messages = messages; - - factory _$_FormNode.fromJson(Map json) => - _$$_FormNodeFromJson(json); - - @override - final NodeAttribute attributes; - final List _messages; - @override - List get messages { - if (_messages is EqualUnmodifiableListView) return _messages; - // ignore: implicit_dynamic_type - return EqualUnmodifiableListView(_messages); - } - - @override - String toString() { - return 'FormNode(attributes: $attributes, messages: $messages)'; - } - - @override - bool operator ==(dynamic other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$_FormNode && - (identical(other.attributes, attributes) || - other.attributes == attributes) && - const DeepCollectionEquality().equals(other._messages, _messages)); - } - - @JsonKey(ignore: true) - @override - int get hashCode => Object.hash( - runtimeType, attributes, const DeepCollectionEquality().hash(_messages)); - - @JsonKey(ignore: true) - @override - @pragma('vm:prefer-inline') - _$$_FormNodeCopyWith<_$_FormNode> get copyWith => - __$$_FormNodeCopyWithImpl<_$_FormNode>(this, _$identity); - - @override - Map toJson() { - return _$$_FormNodeToJson( - this, - ); - } -} - -abstract class _FormNode implements FormNode { - const factory _FormNode( - {required final NodeAttribute attributes, - required final List messages}) = _$_FormNode; - - factory _FormNode.fromJson(Map json) = _$_FormNode.fromJson; - - @override - NodeAttribute get attributes; - @override - List get messages; - @override - @JsonKey(ignore: true) - _$$_FormNodeCopyWith<_$_FormNode> get copyWith => - throw _privateConstructorUsedError; -} diff --git a/flutter-ory-network/lib/entities/form_node.g.dart b/flutter-ory-network/lib/entities/form_node.g.dart deleted file mode 100644 index 7b31b11..0000000 --- a/flutter-ory-network/lib/entities/form_node.g.dart +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright © 2023 Ory Corp -// SPDX-License-Identifier: Apache-2.0 - -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'form_node.dart'; - -// ************************************************************************** -// JsonSerializableGenerator -// ************************************************************************** - -_$_FormNode _$$_FormNodeFromJson(Map json) => _$_FormNode( - attributes: - NodeAttribute.fromJson(json['attributes'] as Map), - messages: (json['messages'] as List) - .map((e) => NodeMessage.fromJson(e as Map)) - .toList(), - ); - -Map _$$_FormNodeToJson(_$_FormNode instance) => - { - 'attributes': instance.attributes, - 'messages': instance.messages, - }; diff --git a/flutter-ory-network/lib/entities/formfield.dart b/flutter-ory-network/lib/entities/formfield.dart deleted file mode 100644 index 6407a49..0000000 --- a/flutter-ory-network/lib/entities/formfield.dart +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright © 2023 Ory Corp -// SPDX-License-Identifier: Apache-2.0 - -import 'package:freezed_annotation/freezed_annotation.dart'; - -part 'formfield.freezed.dart'; - -@freezed -sealed class FormField with _$FormField { - const factory FormField({T? value, String? errorMessage}) = _FormField; -} diff --git a/flutter-ory-network/lib/entities/formfield.freezed.dart b/flutter-ory-network/lib/entities/formfield.freezed.dart deleted file mode 100644 index d8e74a8..0000000 --- a/flutter-ory-network/lib/entities/formfield.freezed.dart +++ /dev/null @@ -1,154 +0,0 @@ -// Copyright © 2023 Ory Corp -// SPDX-License-Identifier: Apache-2.0 - -// coverage:ignore-file -// GENERATED CODE - DO NOT MODIFY BY HAND -// 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 'formfield.dart'; - -// ************************************************************************** -// FreezedGenerator -// ************************************************************************** - -T _$identity(T value) => value; - -final _privateConstructorUsedError = UnsupportedError( - 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); - -/// @nodoc -mixin _$FormField { - T? get value => throw _privateConstructorUsedError; - String? get errorMessage => throw _privateConstructorUsedError; - - @JsonKey(ignore: true) - $FormFieldCopyWith> get copyWith => - throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class $FormFieldCopyWith { - factory $FormFieldCopyWith( - FormField value, $Res Function(FormField) then) = - _$FormFieldCopyWithImpl>; - @useResult - $Res call({T? value, String? errorMessage}); -} - -/// @nodoc -class _$FormFieldCopyWithImpl> - implements $FormFieldCopyWith { - _$FormFieldCopyWithImpl(this._value, this._then); - - // ignore: unused_field - final $Val _value; - // ignore: unused_field - final $Res Function($Val) _then; - - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? value = freezed, - Object? errorMessage = freezed, - }) { - return _then(_value.copyWith( - value: freezed == value - ? _value.value - : value // ignore: cast_nullable_to_non_nullable - as T?, - errorMessage: freezed == errorMessage - ? _value.errorMessage - : errorMessage // ignore: cast_nullable_to_non_nullable - as String?, - ) as $Val); - } -} - -/// @nodoc -abstract class _$$_FormFieldCopyWith - implements $FormFieldCopyWith { - factory _$$_FormFieldCopyWith( - _$_FormField value, $Res Function(_$_FormField) then) = - __$$_FormFieldCopyWithImpl; - @override - @useResult - $Res call({T? value, String? errorMessage}); -} - -/// @nodoc -class __$$_FormFieldCopyWithImpl - extends _$FormFieldCopyWithImpl> - implements _$$_FormFieldCopyWith { - __$$_FormFieldCopyWithImpl( - _$_FormField _value, $Res Function(_$_FormField) _then) - : super(_value, _then); - - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? value = freezed, - Object? errorMessage = freezed, - }) { - return _then(_$_FormField( - value: freezed == value - ? _value.value - : value // ignore: cast_nullable_to_non_nullable - as T?, - errorMessage: freezed == errorMessage - ? _value.errorMessage - : errorMessage // ignore: cast_nullable_to_non_nullable - as String?, - )); - } -} - -/// @nodoc - -class _$_FormField implements _FormField { - const _$_FormField({this.value, this.errorMessage}); - - @override - final T? value; - @override - final String? errorMessage; - - @override - String toString() { - return 'FormField<$T>(value: $value, errorMessage: $errorMessage)'; - } - - @override - bool operator ==(dynamic other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$_FormField && - const DeepCollectionEquality().equals(other.value, value) && - (identical(other.errorMessage, errorMessage) || - other.errorMessage == errorMessage)); - } - - @override - int get hashCode => Object.hash( - runtimeType, const DeepCollectionEquality().hash(value), errorMessage); - - @JsonKey(ignore: true) - @override - @pragma('vm:prefer-inline') - _$$_FormFieldCopyWith> get copyWith => - __$$_FormFieldCopyWithImpl>(this, _$identity); -} - -abstract class _FormField implements FormField { - const factory _FormField({final T? value, final String? errorMessage}) = - _$_FormField; - - @override - T? get value; - @override - String? get errorMessage; - @override - @JsonKey(ignore: true) - _$$_FormFieldCopyWith> get copyWith => - throw _privateConstructorUsedError; -} diff --git a/flutter-ory-network/lib/entities/message.dart b/flutter-ory-network/lib/entities/message.dart deleted file mode 100644 index 58ca045..0000000 --- a/flutter-ory-network/lib/entities/message.dart +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright © 2023 Ory Corp -// SPDX-License-Identifier: Apache-2.0 - -import 'package:freezed_annotation/freezed_annotation.dart'; - -part 'message.freezed.dart'; -part 'message.g.dart'; - -@freezed -class NodeMessage with _$NodeMessage { - const factory NodeMessage( - {required int id, - required String text, - required MessageType type, - @Default('general') String attr}) = _NodeMessage; - - factory NodeMessage.fromJson(Map json) => - _$NodeMessageFromJson(json); -} - -// specifies message type -enum MessageType { info, error, success } diff --git a/flutter-ory-network/lib/entities/message.freezed.dart b/flutter-ory-network/lib/entities/message.freezed.dart deleted file mode 100644 index 3e50f41..0000000 --- a/flutter-ory-network/lib/entities/message.freezed.dart +++ /dev/null @@ -1,211 +0,0 @@ -// Copyright © 2023 Ory Corp -// SPDX-License-Identifier: Apache-2.0 - -// coverage:ignore-file -// GENERATED CODE - DO NOT MODIFY BY HAND -// 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 'message.dart'; - -// ************************************************************************** -// FreezedGenerator -// ************************************************************************** - -T _$identity(T value) => value; - -final _privateConstructorUsedError = UnsupportedError( - 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); - -NodeMessage _$NodeMessageFromJson(Map json) { - return _NodeMessage.fromJson(json); -} - -/// @nodoc -mixin _$NodeMessage { - int get id => throw _privateConstructorUsedError; - String get text => throw _privateConstructorUsedError; - MessageType get type => throw _privateConstructorUsedError; - String get attr => throw _privateConstructorUsedError; - - Map toJson() => throw _privateConstructorUsedError; - @JsonKey(ignore: true) - $NodeMessageCopyWith get copyWith => - throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class $NodeMessageCopyWith<$Res> { - factory $NodeMessageCopyWith( - NodeMessage value, $Res Function(NodeMessage) then) = - _$NodeMessageCopyWithImpl<$Res, NodeMessage>; - @useResult - $Res call({int id, String text, MessageType type, String attr}); -} - -/// @nodoc -class _$NodeMessageCopyWithImpl<$Res, $Val extends NodeMessage> - implements $NodeMessageCopyWith<$Res> { - _$NodeMessageCopyWithImpl(this._value, this._then); - - // ignore: unused_field - final $Val _value; - // ignore: unused_field - final $Res Function($Val) _then; - - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? id = null, - Object? text = null, - Object? type = null, - Object? attr = null, - }) { - return _then(_value.copyWith( - id: null == id - ? _value.id - : id // ignore: cast_nullable_to_non_nullable - as int, - text: null == text - ? _value.text - : text // ignore: cast_nullable_to_non_nullable - as String, - type: null == type - ? _value.type - : type // ignore: cast_nullable_to_non_nullable - as MessageType, - attr: null == attr - ? _value.attr - : attr // ignore: cast_nullable_to_non_nullable - as String, - ) as $Val); - } -} - -/// @nodoc -abstract class _$$_NodeMessageCopyWith<$Res> - implements $NodeMessageCopyWith<$Res> { - factory _$$_NodeMessageCopyWith( - _$_NodeMessage value, $Res Function(_$_NodeMessage) then) = - __$$_NodeMessageCopyWithImpl<$Res>; - @override - @useResult - $Res call({int id, String text, MessageType type, String attr}); -} - -/// @nodoc -class __$$_NodeMessageCopyWithImpl<$Res> - extends _$NodeMessageCopyWithImpl<$Res, _$_NodeMessage> - implements _$$_NodeMessageCopyWith<$Res> { - __$$_NodeMessageCopyWithImpl( - _$_NodeMessage _value, $Res Function(_$_NodeMessage) _then) - : super(_value, _then); - - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? id = null, - Object? text = null, - Object? type = null, - Object? attr = null, - }) { - return _then(_$_NodeMessage( - id: null == id - ? _value.id - : id // ignore: cast_nullable_to_non_nullable - as int, - text: null == text - ? _value.text - : text // ignore: cast_nullable_to_non_nullable - as String, - type: null == type - ? _value.type - : type // ignore: cast_nullable_to_non_nullable - as MessageType, - attr: null == attr - ? _value.attr - : attr // ignore: cast_nullable_to_non_nullable - as String, - )); - } -} - -/// @nodoc -@JsonSerializable() -class _$_NodeMessage implements _NodeMessage { - const _$_NodeMessage( - {required this.id, - required this.text, - required this.type, - this.attr = 'general'}); - - factory _$_NodeMessage.fromJson(Map json) => - _$$_NodeMessageFromJson(json); - - @override - final int id; - @override - final String text; - @override - final MessageType type; - @override - @JsonKey() - final String attr; - - @override - String toString() { - return 'NodeMessage(id: $id, text: $text, type: $type, attr: $attr)'; - } - - @override - bool operator ==(dynamic other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$_NodeMessage && - (identical(other.id, id) || other.id == id) && - (identical(other.text, text) || other.text == text) && - (identical(other.type, type) || other.type == type) && - (identical(other.attr, attr) || other.attr == attr)); - } - - @JsonKey(ignore: true) - @override - int get hashCode => Object.hash(runtimeType, id, text, type, attr); - - @JsonKey(ignore: true) - @override - @pragma('vm:prefer-inline') - _$$_NodeMessageCopyWith<_$_NodeMessage> get copyWith => - __$$_NodeMessageCopyWithImpl<_$_NodeMessage>(this, _$identity); - - @override - Map toJson() { - return _$$_NodeMessageToJson( - this, - ); - } -} - -abstract class _NodeMessage implements NodeMessage { - const factory _NodeMessage( - {required final int id, - required final String text, - required final MessageType type, - final String attr}) = _$_NodeMessage; - - factory _NodeMessage.fromJson(Map json) = - _$_NodeMessage.fromJson; - - @override - int get id; - @override - String get text; - @override - MessageType get type; - @override - String get attr; - @override - @JsonKey(ignore: true) - _$$_NodeMessageCopyWith<_$_NodeMessage> get copyWith => - throw _privateConstructorUsedError; -} diff --git a/flutter-ory-network/lib/entities/message.g.dart b/flutter-ory-network/lib/entities/message.g.dart deleted file mode 100644 index d91445c..0000000 --- a/flutter-ory-network/lib/entities/message.g.dart +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright © 2023 Ory Corp -// SPDX-License-Identifier: Apache-2.0 - -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'message.dart'; - -// ************************************************************************** -// JsonSerializableGenerator -// ************************************************************************** - -_$_NodeMessage _$$_NodeMessageFromJson(Map json) => - _$_NodeMessage( - id: json['id'] as int, - text: json['text'] as String, - type: $enumDecode(_$MessageTypeEnumMap, json['type']), - attr: json['attr'] as String? ?? 'general', - ); - -Map _$$_NodeMessageToJson(_$_NodeMessage instance) => - { - 'id': instance.id, - 'text': instance.text, - 'type': _$MessageTypeEnumMap[instance.type]!, - 'attr': instance.attr, - }; - -const _$MessageTypeEnumMap = { - MessageType.info: 'info', - MessageType.error: 'error', - MessageType.success: 'success', -}; diff --git a/flutter-ory-network/lib/entities/node_attribute.dart b/flutter-ory-network/lib/entities/node_attribute.dart deleted file mode 100644 index 2e98d39..0000000 --- a/flutter-ory-network/lib/entities/node_attribute.dart +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright © 2023 Ory Corp -// SPDX-License-Identifier: Apache-2.0 - -import 'package:freezed_annotation/freezed_annotation.dart'; -part 'node_attribute.freezed.dart'; -part 'node_attribute.g.dart'; - -@freezed -sealed class NodeAttribute with _$NodeAttribute { - const factory NodeAttribute({required String name}) = _NodeAttribute; - - factory NodeAttribute.fromJson(Map json) => - _$NodeAttributeFromJson(json); -} diff --git a/flutter-ory-network/lib/entities/node_attribute.freezed.dart b/flutter-ory-network/lib/entities/node_attribute.freezed.dart deleted file mode 100644 index 0843265..0000000 --- a/flutter-ory-network/lib/entities/node_attribute.freezed.dart +++ /dev/null @@ -1,154 +0,0 @@ -// Copyright © 2023 Ory Corp -// SPDX-License-Identifier: Apache-2.0 - -// coverage:ignore-file -// GENERATED CODE - DO NOT MODIFY BY HAND -// 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 'node_attribute.dart'; - -// ************************************************************************** -// FreezedGenerator -// ************************************************************************** - -T _$identity(T value) => value; - -final _privateConstructorUsedError = UnsupportedError( - 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); - -NodeAttribute _$NodeAttributeFromJson(Map json) { - return _NodeAttribute.fromJson(json); -} - -/// @nodoc -mixin _$NodeAttribute { - String get name => throw _privateConstructorUsedError; - - Map toJson() => throw _privateConstructorUsedError; - @JsonKey(ignore: true) - $NodeAttributeCopyWith get copyWith => - throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class $NodeAttributeCopyWith<$Res> { - factory $NodeAttributeCopyWith( - NodeAttribute value, $Res Function(NodeAttribute) then) = - _$NodeAttributeCopyWithImpl<$Res, NodeAttribute>; - @useResult - $Res call({String name}); -} - -/// @nodoc -class _$NodeAttributeCopyWithImpl<$Res, $Val extends NodeAttribute> - implements $NodeAttributeCopyWith<$Res> { - _$NodeAttributeCopyWithImpl(this._value, this._then); - - // ignore: unused_field - final $Val _value; - // ignore: unused_field - final $Res Function($Val) _then; - - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? name = null, - }) { - return _then(_value.copyWith( - name: null == name - ? _value.name - : name // ignore: cast_nullable_to_non_nullable - as String, - ) as $Val); - } -} - -/// @nodoc -abstract class _$$_NodeAttributeCopyWith<$Res> - implements $NodeAttributeCopyWith<$Res> { - factory _$$_NodeAttributeCopyWith( - _$_NodeAttribute value, $Res Function(_$_NodeAttribute) then) = - __$$_NodeAttributeCopyWithImpl<$Res>; - @override - @useResult - $Res call({String name}); -} - -/// @nodoc -class __$$_NodeAttributeCopyWithImpl<$Res> - extends _$NodeAttributeCopyWithImpl<$Res, _$_NodeAttribute> - implements _$$_NodeAttributeCopyWith<$Res> { - __$$_NodeAttributeCopyWithImpl( - _$_NodeAttribute _value, $Res Function(_$_NodeAttribute) _then) - : super(_value, _then); - - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? name = null, - }) { - return _then(_$_NodeAttribute( - name: null == name - ? _value.name - : name // ignore: cast_nullable_to_non_nullable - as String, - )); - } -} - -/// @nodoc -@JsonSerializable() -class _$_NodeAttribute implements _NodeAttribute { - const _$_NodeAttribute({required this.name}); - - factory _$_NodeAttribute.fromJson(Map json) => - _$$_NodeAttributeFromJson(json); - - @override - final String name; - - @override - String toString() { - return 'NodeAttribute(name: $name)'; - } - - @override - bool operator ==(dynamic other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$_NodeAttribute && - (identical(other.name, name) || other.name == name)); - } - - @JsonKey(ignore: true) - @override - int get hashCode => Object.hash(runtimeType, name); - - @JsonKey(ignore: true) - @override - @pragma('vm:prefer-inline') - _$$_NodeAttributeCopyWith<_$_NodeAttribute> get copyWith => - __$$_NodeAttributeCopyWithImpl<_$_NodeAttribute>(this, _$identity); - - @override - Map toJson() { - return _$$_NodeAttributeToJson( - this, - ); - } -} - -abstract class _NodeAttribute implements NodeAttribute { - const factory _NodeAttribute({required final String name}) = _$_NodeAttribute; - - factory _NodeAttribute.fromJson(Map json) = - _$_NodeAttribute.fromJson; - - @override - String get name; - @override - @JsonKey(ignore: true) - _$$_NodeAttributeCopyWith<_$_NodeAttribute> get copyWith => - throw _privateConstructorUsedError; -} diff --git a/flutter-ory-network/lib/entities/node_attribute.g.dart b/flutter-ory-network/lib/entities/node_attribute.g.dart deleted file mode 100644 index 92070a7..0000000 --- a/flutter-ory-network/lib/entities/node_attribute.g.dart +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright © 2023 Ory Corp -// SPDX-License-Identifier: Apache-2.0 - -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'node_attribute.dart'; - -// ************************************************************************** -// JsonSerializableGenerator -// ************************************************************************** - -_$_NodeAttribute _$$_NodeAttributeFromJson(Map json) => - _$_NodeAttribute( - name: json['name'] as String, - ); - -Map _$$_NodeAttributeToJson(_$_NodeAttribute instance) => - { - 'name': instance.name, - }; diff --git a/flutter-ory-network/lib/main.dart b/flutter-ory-network/lib/main.dart index 529da37..ea93cfc 100644 --- a/flutter-ory-network/lib/main.dart +++ b/flutter-ory-network/lib/main.dart @@ -12,6 +12,7 @@ import 'blocs/auth/auth_bloc.dart'; import 'pages/home.dart'; import 'pages/login.dart'; import 'pages/entry.dart'; + import 'repositories/auth.dart'; import 'services/auth.dart'; @@ -89,7 +90,14 @@ class _MyAppViewState extends State { case AuthStatus.unauthenticated: _navigator.pushAndRemoveUntil( MaterialPageRoute( - builder: (BuildContext context) => const LoginPage()), + builder: (BuildContext context) => + const LoginPage(aal: 'aal1')), + (Route route) => false); + case AuthStatus.aal2Requested: + _navigator.pushAndRemoveUntil( + MaterialPageRoute( + builder: (BuildContext context) => + const LoginPage(aal: 'aal2')), (Route route) => false); case AuthStatus.uninitialized: break; diff --git a/flutter-ory-network/lib/pages/home.dart b/flutter-ory-network/lib/pages/home.dart index 0f18adc..af459fe 100644 --- a/flutter-ory-network/lib/pages/home.dart +++ b/flutter-ory-network/lib/pages/home.dart @@ -78,7 +78,7 @@ class _HomePageState extends State { child: Column( children: [ Text( - "Welcome back,\n${session.identity.id}!", + "Welcome back,\n${session.identity!.id}!", style: const TextStyle(fontWeight: FontWeight.w900, fontSize: 35), ), const Padding( @@ -94,7 +94,7 @@ class _HomePageState extends State { decoration: BoxDecoration( borderRadius: BorderRadius.circular(8), color: Colors.grey[600]), - child: Text(session.identity.traits.toString(), + child: Text(session.identity!.traits.toString(), style: const TextStyle( fontWeight: FontWeight.w500, color: Colors.white)), ), diff --git a/flutter-ory-network/lib/pages/login.dart b/flutter-ory-network/lib/pages/login.dart index ccfdb0c..17dd68f 100644 --- a/flutter-ory-network/lib/pages/login.dart +++ b/flutter-ory-network/lib/pages/login.dart @@ -1,17 +1,17 @@ -// Copyright © 2023 Ory Corp -// SPDX-License-Identifier: Apache-2.0 - import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:ory_client/ory_client.dart'; +import 'package:ory_network_flutter/widgets/nodes/provider.dart'; import '../blocs/auth/auth_bloc.dart'; import '../blocs/login/login_bloc.dart'; import '../repositories/auth.dart'; -import '../widgets/social_provider_box.dart'; +import '../widgets/helpers.dart'; import 'registration.dart'; class LoginPage extends StatelessWidget { - const LoginPage({super.key}); + final String aal; + const LoginPage({super.key, required this.aal}); @override Widget build(BuildContext context) { @@ -21,251 +21,186 @@ class LoginPage extends StatelessWidget { create: (context) => LoginBloc( authBloc: context.read(), repository: RepositoryProvider.of(context)) - ..add(CreateLoginFlow()), + ..add(CreateLoginFlow(aal: aal)), child: const LoginForm()), ); } } -class LoginForm extends StatefulWidget { +class LoginForm extends StatelessWidget { const LoginForm({super.key}); - @override - State createState() => LoginFormState(); -} - -class LoginFormState extends State { - final emailController = TextEditingController(); - final passwordController = TextEditingController(); @override Widget build(BuildContext context) { - final loginBloc = BlocProvider.of(context); - return BlocConsumer( - bloc: loginBloc, - // listen to email and password changes - listenWhen: (previous, current) { - return (previous.email.value != current.email.value && - emailController.text != current.email.value) || - (previous.password.value != current.password.value && - passwordController.text != current.password.value); - }, - // if email or password value have changed, update text controller values - listener: (BuildContext context, LoginState state) { - emailController.text = state.email.value; - passwordController.text = state.password.value; - }, - builder: (context, state) { - // login flow was created - if (state.flowId != null) { - return _buildLoginForm(context, state); - } // otherwise, show loading or error - else { - return _buildLoginFlowNotCreated(context, state); - } - }); + return BlocBuilder( + buildWhen: (previous, current) => previous.isLoading != current.isLoading, + builder: (context, state) { + if (state.loginFlow != null) { + return _buildUi(context, state); + } else { + return buildFlowNotCreated(context, state.message); + } + }, + ); } - _buildLoginFlowNotCreated(BuildContext context, LoginState state) { - if (state.errorMessage != null) { - return Center( - child: Text( - state.errorMessage!, - style: const TextStyle(color: Colors.red), - )); - } else { - return const Center(child: CircularProgressIndicator()); - } - } + _buildUi(BuildContext context, LoginState state) { + final nodes = state.loginFlow!.ui.nodes; - _buildLoginForm(BuildContext context, LoginState state) { - return Padding( - padding: const EdgeInsets.symmetric(horizontal: 32), - child: SingleChildScrollView( - // do not show scrolling indicator - physics: const BouncingScrollPhysics(), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: EdgeInsets.only( - //status bar height + padding - top: MediaQuery.of(context).viewPadding.top + 48), - child: Image.asset( - 'assets/images/ory_logo.png', - width: 70, - ), - ), - const SizedBox( - height: 32, - ), - const Text("Sign in", - style: TextStyle( - fontWeight: FontWeight.w600, height: 1.5, fontSize: 18)), - const Text( - "Sign in with a social provider or with your email and password"), - const SizedBox( - height: 32, - ), - const Row( - children: [ - SocialProviderBox(provider: SocialProvider.google), - SizedBox( - width: 12, - ), - SocialProviderBox(provider: SocialProvider.github), - SizedBox( - width: 12, - ), - SocialProviderBox(provider: SocialProvider.apple), - SizedBox( - width: 12, - ), - SocialProviderBox(provider: SocialProvider.linkedin) - ], - ), - const SizedBox( - height: 32, - ), - const Divider( - color: Color.fromRGBO(226, 232, 240, 1), thickness: 1), - const SizedBox( - height: 32, - ), + // get default nodes from all nodes + final defaultNodes = + nodes.where((node) => node.group == UiNodeGroupEnum.default_).toList(); - const SizedBox( - height: 20, - ), + // get password nodes from all nodes + final passwordNodes = + nodes.where((node) => node.group == UiNodeGroupEnum.password).toList(); - const SizedBox( - height: 32, - ), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Text('Email'), - const SizedBox( - height: 4, - ), - TextFormField( - enabled: !state.isLoading, - controller: emailController, - onChanged: (String value) => - context.read().add(ChangeEmail(value: value)), - decoration: InputDecoration( - border: const OutlineInputBorder(), - hintText: 'Enter your email', - errorText: state.email.errorMessage, - errorMaxLines: 3), - ), - ], - ), - const SizedBox( - height: 20, - ), - Container( - padding: EdgeInsets.zero, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text('Password'), - TextButton( - onPressed: null, child: Text("Forgot password?")) - ], - ), - const SizedBox( - height: 4, - ), - TextFormField( - enabled: !state.isLoading, - controller: passwordController, - onChanged: (String value) => context - .read() - .add(ChangePassword(value: value)), - obscureText: state.isPasswordHidden, - decoration: InputDecoration( - border: const OutlineInputBorder(), - hintText: 'Enter your password', - // change password visibility - suffixIcon: GestureDetector( - onTap: () => context.read().add( - ChangePasswordVisibility( - value: !state.isPasswordHidden)), - child: ImageIcon( - state.isPasswordHidden - ? const AssetImage('assets/icons/eye.png') - : const AssetImage('assets/icons/eye-off.png'), - size: 16, - ), - ), - errorText: state.password.errorMessage, - errorMaxLines: 3), - ), - ], - ), - ), - const SizedBox( - height: 32, - ), - // show general error message if it exists - if (state.errorMessage != null) + // get lookup secret nodes from all nodes + final lookupSecretNodes = nodes + .where((node) => node.group == UiNodeGroupEnum.lookupSecret) + .toList(); + + // get totp nodes from all nodes + final totpNodes = + nodes.where((node) => node.group == UiNodeGroupEnum.totp).toList(); + + // get oidc nodes from all nodes + final oidcNodes = + nodes.where((node) => node.group == UiNodeGroupEnum.oidc).toList(); + + return Stack(children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 32), + child: SingleChildScrollView( + // do not show scrolling indicator + physics: const BouncingScrollPhysics(), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ Padding( - padding: const EdgeInsets.only(bottom: 15.0), - child: Text( - state.errorMessage!, - style: const TextStyle(color: Colors.red), - maxLines: 3, + padding: EdgeInsets.only( + //status bar height + padding + top: MediaQuery.of(context).viewPadding.top + 48), + child: Image.asset( + 'assets/images/ory_logo.png', + width: 70, ), ), - // show loading indicator when state is in a loading mode - if (state.isLoading) - const Padding( - padding: EdgeInsets.all(15.0), - child: Center( - child: SizedBox( - width: 30, - height: 30, - child: CircularProgressIndicator())), + const SizedBox( + height: 32, ), - SizedBox( - width: double.infinity, - child: FilledButton( - // disable button when state is loading - onPressed: state.isLoading - ? null - : () { - context.read().add(LoginWithEmailAndPassword( - flowId: state.flowId!, - email: state.email.value, - password: state.password.value)); - }, - child: const Text('Sign in'), + const Text('Sign in', + style: TextStyle( + fontWeight: FontWeight.w600, height: 1.5, fontSize: 18)), + if (oidcNodes.isNotEmpty) + Padding( + padding: const EdgeInsets.only(top: 32.0), + child: Column( + mainAxisSize: MainAxisSize.max, + children: oidcNodes + .map((node) => SocialProviderInput(node: node)) + .toList()), + ), + if (defaultNodes.isNotEmpty) + buildGroup(context, UiNodeGroupEnum.default_, + defaultNodes, _onInputChange, _onInputSubmit), + if (passwordNodes.isNotEmpty) + buildGroup(context, UiNodeGroupEnum.password, + passwordNodes, _onInputChange, _onInputSubmit), + if (lookupSecretNodes.isNotEmpty) + buildGroup(context, UiNodeGroupEnum.lookupSecret, + lookupSecretNodes, _onInputChange, _onInputSubmit), + if (totpNodes.isNotEmpty) + buildGroup(context, UiNodeGroupEnum.totp, totpNodes, + _onInputChange, _onInputSubmit), + const SizedBox( + height: 32, ), - ), - const SizedBox( - height: 32, - ), - Row( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - const Text('No account?'), - TextButton( - // disable button when state is loading - onPressed: state.isLoading - ? null - : () => Navigator.of(context).pushReplacement( - MaterialPageRoute( - builder: (context) => - const RegistrationPage())), - child: const Text('Sign up')) - ], - ) - ], + if (state.loginFlow?.ui.messages != null) + for (var message in state.loginFlow!.ui.messages!) + Text( + message.text, + style: TextStyle(color: getMessageColor(message.type)), + ), + // build progress indicator when state is loading + BlocSelector( + bloc: (context).read(), + selector: (AuthState state) => + state.status == AuthStatus.aal2Requested, + builder: (BuildContext context, bool booleanState) { + print(booleanState); + if (booleanState) { + return Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + const Text('Something\'s not working?'), + TextButton( + // disable button when state is loading + onPressed: () => + (context).read().add(LogOut()), + child: const Text('Logout')) + ], + ); + } else { + return Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + const Text('No account?'), + TextButton( + // disable button when state is loading + onPressed: state.isLoading + ? null + : () => Navigator.of(context).pushReplacement( + MaterialPageRoute( + builder: (context) => + const RegistrationPage())), + child: const Text('Sign up')) + ], + ); + } + }), + ], + ), ), ), - ); + // build progress indicator when state is loading + BlocSelector( + bloc: (context).read(), + selector: (LoginState state) => state.isLoading, + builder: (BuildContext context, bool booleanState) { + if (booleanState) { + return const Opacity( + opacity: 0.8, + child: ModalBarrier(dismissible: false, color: Colors.white30), + ); + } else { + return Container(); + } + }), + BlocSelector( + bloc: (context).read(), + selector: (LoginState state) => state.isLoading, + builder: (BuildContext context, bool booleanState) { + if (booleanState) { + return const Center( + child: CircularProgressIndicator(), + ); + } else { + return Container(); + } + }) + ]); + } + + _onInputChange(BuildContext context, String value, String name) { + context.read().add(ChangeNodeValue(value: value, name: name)); + } + + _onInputSubmit( + BuildContext context, UiNodeGroupEnum group, String name, String value) { + context + .read() + .add(UpdateLoginFlow(group: group, name: name, value: value)); } } diff --git a/flutter-ory-network/lib/pages/registration.dart b/flutter-ory-network/lib/pages/registration.dart index 5446594..72c84e5 100644 --- a/flutter-ory-network/lib/pages/registration.dart +++ b/flutter-ory-network/lib/pages/registration.dart @@ -3,11 +3,13 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:ory_network_flutter/widgets/social_provider_box.dart'; +import 'package:ory_client/ory_client.dart'; import '../blocs/auth/auth_bloc.dart'; import '../blocs/registration/registration_bloc.dart'; import '../repositories/auth.dart'; +import '../widgets/helpers.dart'; +import '../widgets/nodes/provider.dart'; import 'login.dart'; class RegistrationPage extends StatelessWidget { @@ -39,214 +41,160 @@ class RegistrationFormState extends State { @override Widget build(BuildContext context) { - final registrationBloc = BlocProvider.of(context); - return BlocConsumer( - bloc: registrationBloc, - // listen to email and password changes - listenWhen: (previous, current) { - return (previous.email.value != current.email.value && - emailController.text != current.email.value) || - (previous.password.value != current.password.value && - passwordController.text != current.password.value); - }, - // if email or password value have changed, update text controller values - listener: (BuildContext context, RegistrationState state) { - emailController.text = state.email.value; - passwordController.text = state.password.value; - }, - builder: (context, state) { - // registration flow was created - if (state.flowId != null) { - return _buildRegistrationForm(context, state); - } // otherwise, show loading or error - else { - return _buildRegistrationFlowNotCreated(context, state); - } - }); + return BlocBuilder( + buildWhen: (previous, current) => previous.isLoading != current.isLoading, + builder: (context, state) { + if (state.registrationFlow != null) { + return _buildUi(context, state); + } else { + return buildFlowNotCreated(context, state.message); + } + }, + ); } - _buildRegistrationFlowNotCreated( - BuildContext context, RegistrationState state) { - if (state.errorMessage != null) { - return Center( - child: Text( - state.errorMessage!, - style: const TextStyle(color: Colors.red), - )); - } else { - return const Center(child: CircularProgressIndicator()); - } - } + _buildUi(BuildContext context, RegistrationState state) { + final nodes = state.registrationFlow!.ui.nodes; - _buildRegistrationForm(BuildContext context, RegistrationState state) { - return Padding( - padding: const EdgeInsets.symmetric(horizontal: 32), - child: SingleChildScrollView( - // do not show scrolling indicator - physics: const BouncingScrollPhysics(), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: EdgeInsets.only( - //status bar height + padding - top: MediaQuery.of(context).viewPadding.top + 48), - child: Image.asset( - 'assets/images/ory_logo.png', - width: 70, - ), - ), - const SizedBox( - height: 32, - ), - const Text("Sign up", - style: TextStyle( - fontWeight: FontWeight.w600, height: 1.5, fontSize: 18)), - const Text("Sign up with a social provider or with your email"), - const SizedBox( - height: 32, - ), - const Row( - children: [ - SocialProviderBox(provider: SocialProvider.google), - SizedBox( - width: 12, - ), - SocialProviderBox(provider: SocialProvider.github), - SizedBox( - width: 12, - ), - SocialProviderBox(provider: SocialProvider.apple), - SizedBox( - width: 12, - ), - SocialProviderBox(provider: SocialProvider.linkedin) - ], - ), - const SizedBox( - height: 32, - ), - const Divider( - color: Color.fromRGBO(226, 232, 240, 1), thickness: 1), - const SizedBox( - height: 32, - ), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Text('Email'), - const SizedBox( - height: 4, - ), - TextFormField( - enabled: !state.isLoading, - controller: emailController, - onChanged: (String value) => context - .read() - .add(ChangeEmail(value: value)), - decoration: InputDecoration( - border: const OutlineInputBorder(), - hintText: 'Enter your email', - errorText: state.email.errorMessage, - errorMaxLines: 3), - ), - ], - ), - const SizedBox( - height: 20, - ), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Text('Password'), - const SizedBox( - height: 4, - ), - TextFormField( - enabled: !state.isLoading, - controller: passwordController, - onChanged: (String value) => context - .read() - .add(ChangePassword(value: value)), - obscureText: state.isPasswordHidden, - decoration: InputDecoration( - border: const OutlineInputBorder(), - hintText: 'Enter a password', - // change password visibility - suffixIcon: GestureDetector( - onTap: () => context.read().add( - ChangePasswordVisibility( - value: !state.isPasswordHidden)), - child: ImageIcon( - state.isPasswordHidden - ? const AssetImage('assets/icons/eye.png') - : const AssetImage('assets/icons/eye-off.png'), - size: 16, - ), - ), - errorText: state.password.errorMessage, - errorMaxLines: 3), - ), - ], - ), - const SizedBox( - height: 32, - ), - // show general error message if it exists - if (state.errorMessage != null) + // get default nodes from all nodes + final defaultNodes = + nodes.where((node) => node.group == UiNodeGroupEnum.default_).toList(); + + // get password nodes from all nodes + final passwordNodes = + nodes.where((node) => node.group == UiNodeGroupEnum.password).toList(); + + // get lookup secret nodes from all nodes + final lookupSecretNodes = nodes + .where((node) => node.group == UiNodeGroupEnum.lookupSecret) + .toList(); + + // get totp nodes from all nodes + final totpNodes = + nodes.where((node) => node.group == UiNodeGroupEnum.totp).toList(); + + // get oidc nodes from all nodes + final oidcNodes = + nodes.where((node) => node.group == UiNodeGroupEnum.oidc).toList(); + + return Stack(children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 32), + child: SingleChildScrollView( + // do not show scrolling indicator + physics: const BouncingScrollPhysics(), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ Padding( - padding: const EdgeInsets.only(bottom: 15.0), - child: Text( - state.errorMessage!, - style: const TextStyle(color: Colors.red), + padding: EdgeInsets.only( + //status bar height + padding + top: MediaQuery.of(context).viewPadding.top + 48), + child: Image.asset( + 'assets/images/ory_logo.png', + width: 70, ), ), - // show loading indicator when state is in a loading mode - if (state.isLoading) - const Padding( - padding: EdgeInsets.all(15.0), - child: Center( - child: SizedBox( - width: 30, - height: 30, - child: CircularProgressIndicator())), + const SizedBox( + height: 32, + ), + const Text('Sign up', + style: TextStyle( + fontWeight: FontWeight.w600, height: 1.5, fontSize: 18)), + if (oidcNodes.isNotEmpty) + Padding( + padding: const EdgeInsets.only(top: 32.0), + child: Column( + mainAxisSize: MainAxisSize.max, + children: oidcNodes + .map((node) => SocialProviderInput(node: node)) + .toList()), + ), + if (defaultNodes.isNotEmpty) + buildGroup(context, UiNodeGroupEnum.default_, + defaultNodes, _onInputChange, _onInputSubmit), + if (passwordNodes.isNotEmpty) + buildGroup(context, UiNodeGroupEnum.password, + passwordNodes, _onInputChange, _onInputSubmit), + if (lookupSecretNodes.isNotEmpty) + buildGroup( + context, + UiNodeGroupEnum.lookupSecret, + lookupSecretNodes, + _onInputChange, + _onInputSubmit), + if (totpNodes.isNotEmpty) + buildGroup(context, UiNodeGroupEnum.totp, + totpNodes, _onInputChange, _onInputSubmit), + const SizedBox( + height: 32, ), - SizedBox( - width: double.infinity, - child: FilledButton( - // disable button when state is loading - onPressed: state.isLoading - ? null - : () { - context.read().add( - RegisterWithEmailAndPassword( - flowId: state.flowId!, - email: state.email.value, - password: state.password.value)); - }, - child: const Text('Sign up')), - ), - const SizedBox( - height: 20, - ), - Row( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - const Text('Already have an account?'), - TextButton( - // disable button when state is loading - onPressed: state.isLoading - ? null - : () => Navigator.of(context).pushReplacement( - MaterialPageRoute( - builder: (context) => const LoginPage())), - child: const Text('Sign in')) - ], - ) - ], + if (state.registrationFlow?.ui.messages != null) + for (var message in state.registrationFlow!.ui.messages!) + Text( + message.text, + style: TextStyle(color: getMessageColor(message.type)), + ), + Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + const Text('Already have an account?'), + TextButton( + // disable button when state is loading + onPressed: state.isLoading + ? null + : () => Navigator.of(context) + .pushReplacement(MaterialPageRoute( + builder: (context) => const LoginPage( + aal: 'aal1', + ))), + child: const Text('Sign in')) + ], + ) + ], + ), ), ), - ); + // build progress indicator when state is loading + BlocSelector( + bloc: (context).read(), + selector: (RegistrationState state) => state.isLoading, + builder: (BuildContext context, bool booleanState) { + if (booleanState) { + return const Opacity( + opacity: 0.8, + child: ModalBarrier(dismissible: false, color: Colors.white30), + ); + } else { + return Container(); + } + }), + BlocSelector( + bloc: (context).read(), + selector: (RegistrationState state) => state.isLoading, + builder: (BuildContext context, bool booleanState) { + if (booleanState) { + return const Center( + child: CircularProgressIndicator(), + ); + } else { + return Container(); + } + }) + ]); + } + + _onInputChange(BuildContext context, String value, String name) { + context + .read() + .add(ChangeNodeValue(value: value, name: name)); + } + + _onInputSubmit( + BuildContext context, UiNodeGroupEnum group, String name, String value) { + context + .read() + .add(UpdateRegistrationFlow(group: group, name: name, value: value)); } } diff --git a/flutter-ory-network/lib/repositories/auth.dart b/flutter-ory-network/lib/repositories/auth.dart index 85e03ad..8c92006 100644 --- a/flutter-ory-network/lib/repositories/auth.dart +++ b/flutter-ory-network/lib/repositories/auth.dart @@ -1,11 +1,16 @@ // Copyright © 2023 Ory Corp // SPDX-License-Identifier: Apache-2.0 +import 'package:built_collection/built_collection.dart'; +import 'package:built_value/json_object.dart'; +import 'package:deep_collection/deep_collection.dart'; +import 'package:one_of/one_of.dart'; import 'package:ory_client/ory_client.dart'; +import 'package:collection/collection.dart'; import '../services/auth.dart'; -enum AuthStatus { uninitialized, authenticated, unauthenticated } +enum AuthStatus { uninitialized, authenticated, unauthenticated, aal2Requested } class AuthRepository { final AuthService service; @@ -17,33 +22,187 @@ class AuthRepository { return session; } - Future createLoginFlow() async { - final flowId = await service.createLoginFlow(); - return flowId; + Future createLoginFlow({required String aal}) async { + final flow = await service.createLoginFlow(aal: aal); + return flow; } - Future createRegistrationFlow() async { - final flowId = await service.createRegistrationFlow(); - return flowId; + Future createRegistrationFlow() async { + final flow = await service.createRegistrationFlow(); + return flow; } - Future loginWithEmailAndPassword( - {required String flowId, - required String email, - required String password}) async { - await service.loginWithEmailAndPassword( - flowId: flowId, email: email, password: password); + Future getLoginFlow({required String flowId}) async { + final flow = await service.getLoginFlow(flowId: flowId); + return flow; } - Future registerWithEmailAndPassword( - {required String flowId, - required String email, - required String password}) async { - await service.registerWithEmailAndPassword( - flowId: flowId, email: email, password: password); + Future getRegistrationFlow({required String flowId}) async { + final flow = await service.getRegistrationFlow(flowId: flowId); + return flow; } Future logout() async { await service.logout(); } + + Future updateRegistrationFlow( + {required String flowId, + required UiNodeGroupEnum group, + required String name, + required String value, + required List nodes}) async { + // create request body + final body = _createRequestBody( + group: group, name: name, value: value, nodes: nodes); + // submit registration + final registration = await service.updateRegistrationFlow( + flowId: flowId, group: group, value: body); + return registration; + } + + Future updateLoginFlow( + {required String flowId, + required UiNodeGroupEnum group, + required String name, + required String value, + required List nodes}) async { + // create request body + final body = _createRequestBody( + group: group, name: name, value: value, nodes: nodes); + // submit login + print(body); + final login = await service.updateLoginFlow( + flowId: flowId, group: group, value: body); + return login; + } + + Map _createRequestBody( + {required UiNodeGroupEnum group, + required String name, + required String value, + required List nodes}) { + // if name of submitted node is method, find all nodes that belong to the group + if (name == 'method') { + // get input nodes of the same group + final inputNodes = nodes.where((p0) { + if (p0.attributes.oneOf.isType(UiNodeInputAttributes)) { + final attributes = p0.attributes.oneOf.value as UiNodeInputAttributes; + // if group is password, find identifier + if (group == UiNodeGroupEnum.password && + p0.group == UiNodeGroupEnum.default_ && + attributes.name == 'identifier') { + return true; + } + return p0.group == group && + attributes.type != UiNodeInputAttributesTypeEnum.button && + attributes.type != UiNodeInputAttributesTypeEnum.submit; + } else { + return false; + } + }); + // create maps from attribute names and their values + final nestedMaps = inputNodes.map((e) { + final attributes = e.attributes.oneOf.value as UiNodeInputAttributes; + + return generateNestedMap( + attributes.name, attributes.value?.asString ?? ''); + }).toList(); + + // merge nested maps into one + final mergedMap = + nestedMaps.reduce((value, element) => value.deepMerge(element)); + + return mergedMap; + } else { + return {name: value}; + } + } + + LoginFlow resetButtonValues({required LoginFlow loginFlow}) { + final submitInputNodes = loginFlow.ui.nodes.where((p0) { + if (p0.attributes.oneOf.isType(UiNodeInputAttributes)) { + final attributes = p0.attributes.oneOf.value as UiNodeInputAttributes; + final type = attributes.type; + if ((type == UiNodeInputAttributesTypeEnum.button || + type == UiNodeInputAttributesTypeEnum.submit) && + attributes.value?.asString == 'true') { + return true; + } + } + return false; + }); + for (var node in submitInputNodes) { + final attributes = node.attributes.oneOf.value as UiNodeInputAttributes; + // reset button value to false + // to prevent submitting values that were not selected + loginFlow = changeLoginNodeValue( + settings: loginFlow, name: attributes.name, value: 'false'); + } + return loginFlow; + } + + RegistrationFlow changeRegistrationNodeValue( + {required RegistrationFlow settings, + required String name, + required String value}) { + // update node value + final updatedNodes = + updateNodes(nodes: settings.ui.nodes, name: name, value: value); + // update settings' node + final newFlow = + settings.rebuild((p0) => p0..ui.nodes.replace(updatedNodes)); + return newFlow; + } + + LoginFlow changeLoginNodeValue( + {required LoginFlow settings, + required String name, + required String value}) { + // update node value + final updatedNodes = + updateNodes(nodes: settings.ui.nodes, name: name, value: value); + // update settings' node + final newFlow = + settings.rebuild((p0) => p0..ui.nodes.replace(updatedNodes)); + return newFlow; + } + + BuiltList updateNodes( + {required BuiltList nodes, + required String name, + required String value}) { + // get edited node + final node = nodes.firstWhereOrNull((element) { + if (element.attributes.oneOf.isType(UiNodeInputAttributes)) { + return (element.attributes.oneOf.value as UiNodeInputAttributes).name == + name; + } else { + return false; + } + }); + + // udate value of edited node + final updatedNode = node?.rebuild((p0) => p0 + ..attributes.update((b) { + final oldValue = b.oneOf?.value as UiNodeInputAttributes; + final newValue = oldValue.rebuild((p0) => p0.value = JsonObject(value)); + b.oneOf = OneOf1(value: newValue); + })); + // get index of to be updated node + final nodeIndex = nodes.indexOf(node!); + // update list of nodes to iclude updated node + return nodes.rebuild((p0) => p0 + ..removeAt(nodeIndex) + ..insert(nodeIndex, updatedNode!)); + } + + Map generateNestedMap(String path, String value) { + var steps = path.split('.'); + Object? result = value; + for (var step in steps.reversed) { + result = {step: result}; + } + return result as Map; + } } diff --git a/flutter-ory-network/lib/services/auth.dart b/flutter-ory-network/lib/services/auth.dart index 2a65bb4..43cffef 100644 --- a/flutter-ory-network/lib/services/auth.dart +++ b/flutter-ory-network/lib/services/auth.dart @@ -6,9 +6,7 @@ import 'package:dio/dio.dart'; import 'package:one_of/one_of.dart'; import 'package:ory_client/ory_client.dart'; -import '../entities/form_node.dart'; import 'exceptions.dart'; -import '../entities/message.dart'; import 'storage.dart'; class AuthService { @@ -21,6 +19,7 @@ class AuthService { try { final token = await storage.getToken(); final response = await _ory.toSession(xSessionToken: token); + if (response.data != null) { // return session return response.data!; @@ -31,6 +30,12 @@ class AuthService { if (e.response?.statusCode == 401) { await storage.deleteToken(); throw const CustomException.unauthorized(); + } else if (e.response?.statusCode == 403) { + if (e.response?.data['error']['id'] == 'session_aal2_required') { + throw const CustomException.twoFactorAuthRequired(); + } else { + throw _handleUnknownException(e.response?.data); + } } else { throw _handleUnknownException(e.response?.data); } @@ -38,12 +43,14 @@ class AuthService { } /// Create login flow - Future createLoginFlow() async { + Future createLoginFlow({required String aal}) async { try { - final response = await _ory.createNativeLoginFlow(); + final token = await storage.getToken(); + final response = + await _ory.createNativeLoginFlow(aal: aal, xSessionToken: token); if (response.data != null) { // return flow id - return response.data!.id; + return response.data!; } else { throw const CustomException.unknown(); } @@ -53,12 +60,12 @@ class AuthService { } /// Create registration flow - Future createRegistrationFlow() async { + Future createRegistrationFlow() async { try { final response = await _ory.createNativeRegistrationFlow(); if (response.data != null) { // return flow id - return response.data!.id; + return response.data!; } else { throw const CustomException.unknown(); } @@ -67,27 +74,53 @@ class AuthService { } } - /// Log in with [email] and [password] using login flow with [flowId] - Future loginWithEmailAndPassword( + /// Update login flow with [flowId] for [group] with [value] + Future updateLoginFlow( {required String flowId, - required String email, - required String password}) async { + required UiNodeGroupEnum group, + required Map value}) async { try { - final UpdateLoginFlowWithPasswordMethod loginFLowBuilder = - UpdateLoginFlowWithPasswordMethod((b) => b - ..identifier = email - ..password = password - ..method = 'password'); + final token = await storage.getToken(); + final OneOf oneOf; + + // create update body depending on method + switch (group) { + case UiNodeGroupEnum.password: + oneOf = OneOf.fromValue1( + value: UpdateLoginFlowWithPasswordMethod((b) => b + ..method = group.name + ..identifier = value['identifier'] + ..password = value['password'])); + case UiNodeGroupEnum.lookupSecret: + oneOf = OneOf.fromValue1( + value: UpdateLoginFlowWithLookupSecretMethod((b) => b + ..method = group.name + ..lookupSecret = value['lookup_secret'])); + case UiNodeGroupEnum.totp: + oneOf = OneOf.fromValue1( + value: UpdateLoginFlowWithTotpMethod((b) => b + ..method = group.name + ..totpCode = value['totp_code'])); + + // if method is not implemented, throw exception + default: + throw const CustomException.unknown(); + } final response = await _ory.updateLoginFlow( flow: flowId, - updateLoginFlowBody: UpdateLoginFlowBody( - (b) => b..oneOf = OneOf.fromValue1(value: loginFLowBuilder))); + xSessionToken: token, + updateLoginFlowBody: UpdateLoginFlowBody((b) => b..oneOf = oneOf)); if (response.data?.session != null) { // save session token after successful login await storage.persistToken(response.data!.sessionToken!); - return; + if (response.data!.session.identity != null) { + return response.data!; + } else { + throw CustomException.twoFactorAuthRequired( + session: response.data!.session); + } } else { throw const CustomException.unknown(); } @@ -96,39 +129,96 @@ class AuthService { await storage.deleteToken(); throw const CustomException.unauthorized(); } else if (e.response?.statusCode == 400) { - final messages = _checkFormForErrors(e.response?.data); - throw CustomException.badRequest(messages: messages); + final loginFlow = standardSerializers.deserializeWith( + LoginFlow.serializer, e.response?.data); + if (loginFlow != null) { + throw CustomException.badRequest(flow: loginFlow); + } else { + throw const CustomException.unknown(); + } } else if (e.response?.statusCode == 410) { - // login flow expired, use new flow id and add error message + // settings flow expired, use new flow id and add error message throw CustomException.flowExpired( - flowId: e.response?.data['use_flow_id'], - message: 'Login flow has expired. Please enter credentials again.'); + flowId: e.response?.data['use_flow_id']); + } else { + throw _handleUnknownException(e.response?.data); + } + } + } + + /// Get login flow with [flowId] + Future getLoginFlow({required String flowId}) async { + try { + final response = await _ory.getLoginFlow(id: flowId); + + if (response.data != null) { + // return flow + return response.data!; + } else { + throw const CustomException.unknown(); + } + } on DioException catch (e) { + if (e.response?.statusCode == 401) { + await storage.deleteToken(); + throw const CustomException.unauthorized(); } else { throw _handleUnknownException(e.response?.data); } } } - /// Register with [email] and [password] using registration flow with [flowId] - Future registerWithEmailAndPassword( + /// Get registration flow with [flowId] + Future getRegistrationFlow({required String flowId}) async { + try { + final response = await _ory.getRegistrationFlow(id: flowId); + + if (response.data != null) { + // return flow + return response.data!; + } else { + throw const CustomException.unknown(); + } + } on DioException catch (e) { + if (e.response?.statusCode == 401) { + await storage.deleteToken(); + throw const CustomException.unauthorized(); + } else { + throw _handleUnknownException(e.response?.data); + } + } + } + + /// Update registration flow with [flowId] for [group] with [value] + Future updateRegistrationFlow( {required String flowId, - required String email, - required String password}) async { + required UiNodeGroupEnum group, + required Map value}) async { try { - final UpdateRegistrationFlowWithPasswordMethod registrationFLow = - UpdateRegistrationFlowWithPasswordMethod((b) => b - ..traits = JsonObject({'email': email}) - ..password = password - ..method = 'password'); + final OneOf oneOf; + + // create update body depending on method + switch (group) { + case UiNodeGroupEnum.password: + oneOf = OneOf.fromValue1( + value: UpdateRegistrationFlowWithPasswordMethod((b) => b + ..method = group.name + ..traits = JsonObject(value['traits']) + ..password = value['password'])); + + // if method is not implemented, throw exception + default: + throw const CustomException.unknown(); + } final response = await _ory.updateRegistrationFlow( flow: flowId, - updateRegistrationFlowBody: UpdateRegistrationFlowBody( - (b) => b..oneOf = OneOf.fromValue1(value: registrationFLow))); - if (response.data?.sessionToken != null) { + updateRegistrationFlowBody: + UpdateRegistrationFlowBody((b) => b..oneOf = oneOf)); + + if (response.data?.session != null) { // save session token after successful login await storage.persistToken(response.data!.sessionToken!); - return; + return response.data!; } else { throw const CustomException.unknown(); } @@ -137,14 +227,18 @@ class AuthService { await storage.deleteToken(); throw const CustomException.unauthorized(); } else if (e.response?.statusCode == 400) { - final messages = _checkFormForErrors(e.response?.data); - throw CustomException.badRequest(messages: messages); + final registrationFlow = standardSerializers.deserializeWith( + RegistrationFlow.serializer, e.response?.data); + if (registrationFlow != null) { + throw CustomException.badRequest( + flow: registrationFlow); + } else { + throw const CustomException.unknown(); + } } else if (e.response?.statusCode == 410) { - // registration flow expired, use new flow id and add error message + // settings flow expired, use new flow id and add error message throw CustomException.flowExpired( - flowId: e.response?.data['use_flow_id'], - message: - 'Registration flow has expired. Please enter credentials again.'); + flowId: e.response?.data['use_flow_id']); } else { throw _handleUnknownException(e.response?.data); } @@ -173,39 +267,6 @@ class AuthService { } } - /// Search for error messages and their context in [response] - List _checkFormForErrors(Map response) { - final ui = Map.from(response['ui']); - final nodeList = ui['nodes'] as List; - - // parse ui nodes - final nodes = nodeList.map((e) => FormNode.fromJson(e)).toList(); - - // get only nodes that have messages - final nonEmptyNodes = - nodes.where((element) => element.messages.isNotEmpty).toList(); - - // get node messages - final nodeMessages = nonEmptyNodes - .map((node) => node.messages - .map((msg) => msg.copyWith(attr: node.attributes.name)) - .toList()) - .toList(); - - // get general message if it exists - if (ui['messages'] != null) { - final messageList = ui['messages'] as List; - final messages = - messageList.map((e) => NodeMessage.fromJson(e)).toList(); - nodeMessages.add(messages); - } - // flatten message lists - final flattedNodeMessages = - nodeMessages.expand((element) => element).toList(); - - return flattedNodeMessages; - } - CustomException _handleUnknownException(Map? response) { // use error message if response contains it, otherwise use default value if (response != null && response.containsKey('error')) { diff --git a/flutter-ory-network/lib/services/exceptions.dart b/flutter-ory-network/lib/services/exceptions.dart index 8f058d1..677d5b9 100644 --- a/flutter-ory-network/lib/services/exceptions.dart +++ b/flutter-ory-network/lib/services/exceptions.dart @@ -3,23 +3,20 @@ import 'package:flutter/foundation.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; - -import '../entities/message.dart'; +import 'package:ory_client/ory_client.dart'; part 'exceptions.freezed.dart'; @freezed -sealed class CustomException with _$CustomException { +sealed class CustomException with _$CustomException { const CustomException._(); - const factory CustomException.badRequest( - {List? messages, - @Default(400) int statusCode}) = BadRequestException; - const factory CustomException.unauthorized({@Default(401) int statusCode}) = - UnauthorizedException; + const factory CustomException.badRequest({required T flow}) = + BadRequestException; + const factory CustomException.unauthorized() = UnauthorizedException; const factory CustomException.flowExpired( - {@Default(410) int statusCode, - required String flowId, - String? message}) = FlowExpiredException; + {required String flowId, String? message}) = FlowExpiredException; + const factory CustomException.twoFactorAuthRequired({Session? session}) = + TwoFactorAuthRequiredException; const factory CustomException.unknown( {@Default('An error occured. Please try again later.') String? message}) = UnknownException; diff --git a/flutter-ory-network/lib/services/exceptions.freezed.dart b/flutter-ory-network/lib/services/exceptions.freezed.dart index 08979de..01e7d71 100644 --- a/flutter-ory-network/lib/services/exceptions.freezed.dart +++ b/flutter-ory-network/lib/services/exceptions.freezed.dart @@ -1,6 +1,3 @@ -// Copyright © 2023 Ory Corp -// SPDX-License-Identifier: Apache-2.0 - // coverage:ignore-file // GENERATED CODE - DO NOT MODIFY BY HAND // ignore_for_file: type=lint @@ -18,73 +15,78 @@ final _privateConstructorUsedError = UnsupportedError( 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); /// @nodoc -mixin _$CustomException { +mixin _$CustomException { @optionalTypeArgs TResult when({ - required TResult Function(List? messages, int statusCode) - badRequest, - required TResult Function(int statusCode) unauthorized, - required TResult Function(int statusCode, String flowId, String? message) - flowExpired, + required TResult Function(T flow) badRequest, + required TResult Function() unauthorized, + required TResult Function(String flowId, String? message) flowExpired, + required TResult Function(Session? session) twoFactorAuthRequired, required TResult Function(String? message) unknown, }) => throw _privateConstructorUsedError; @optionalTypeArgs TResult? whenOrNull({ - TResult? Function(List? messages, int statusCode)? badRequest, - TResult? Function(int statusCode)? unauthorized, - TResult? Function(int statusCode, String flowId, String? message)? - flowExpired, + TResult? Function(T flow)? badRequest, + TResult? Function()? unauthorized, + TResult? Function(String flowId, String? message)? flowExpired, + TResult? Function(Session? session)? twoFactorAuthRequired, TResult? Function(String? message)? unknown, }) => throw _privateConstructorUsedError; @optionalTypeArgs TResult maybeWhen({ - TResult Function(List? messages, int statusCode)? badRequest, - TResult Function(int statusCode)? unauthorized, - TResult Function(int statusCode, String flowId, String? message)? - flowExpired, + TResult Function(T flow)? badRequest, + TResult Function()? unauthorized, + TResult Function(String flowId, String? message)? flowExpired, + TResult Function(Session? session)? twoFactorAuthRequired, TResult Function(String? message)? unknown, required TResult orElse(), }) => throw _privateConstructorUsedError; @optionalTypeArgs TResult map({ - required TResult Function(BadRequestException value) badRequest, - required TResult Function(UnauthorizedException value) unauthorized, - required TResult Function(FlowExpiredException value) flowExpired, - required TResult Function(UnknownException value) unknown, + required TResult Function(BadRequestException value) badRequest, + required TResult Function(UnauthorizedException value) unauthorized, + required TResult Function(FlowExpiredException value) flowExpired, + required TResult Function(TwoFactorAuthRequiredException value) + twoFactorAuthRequired, + required TResult Function(UnknownException value) unknown, }) => throw _privateConstructorUsedError; @optionalTypeArgs TResult? mapOrNull({ - TResult? Function(BadRequestException value)? badRequest, - TResult? Function(UnauthorizedException value)? unauthorized, - TResult? Function(FlowExpiredException value)? flowExpired, - TResult? Function(UnknownException value)? unknown, + TResult? Function(BadRequestException value)? badRequest, + TResult? Function(UnauthorizedException value)? unauthorized, + TResult? Function(FlowExpiredException value)? flowExpired, + TResult? Function(TwoFactorAuthRequiredException value)? + twoFactorAuthRequired, + TResult? Function(UnknownException value)? unknown, }) => throw _privateConstructorUsedError; @optionalTypeArgs TResult maybeMap({ - TResult Function(BadRequestException value)? badRequest, - TResult Function(UnauthorizedException value)? unauthorized, - TResult Function(FlowExpiredException value)? flowExpired, - TResult Function(UnknownException value)? unknown, + TResult Function(BadRequestException value)? badRequest, + TResult Function(UnauthorizedException value)? unauthorized, + TResult Function(FlowExpiredException value)? flowExpired, + TResult Function(TwoFactorAuthRequiredException value)? + twoFactorAuthRequired, + TResult Function(UnknownException value)? unknown, required TResult orElse(), }) => throw _privateConstructorUsedError; } /// @nodoc -abstract class $CustomExceptionCopyWith<$Res> { +abstract class $CustomExceptionCopyWith { factory $CustomExceptionCopyWith( - CustomException value, $Res Function(CustomException) then) = - _$CustomExceptionCopyWithImpl<$Res, CustomException>; + CustomException value, $Res Function(CustomException) then) = + _$CustomExceptionCopyWithImpl>; } /// @nodoc -class _$CustomExceptionCopyWithImpl<$Res, $Val extends CustomException> - implements $CustomExceptionCopyWith<$Res> { +class _$CustomExceptionCopyWithImpl> + implements $CustomExceptionCopyWith { _$CustomExceptionCopyWithImpl(this._value, this._then); // ignore: unused_field @@ -94,136 +96,113 @@ class _$CustomExceptionCopyWithImpl<$Res, $Val extends CustomException> } /// @nodoc -abstract class _$$BadRequestExceptionCopyWith<$Res> { - factory _$$BadRequestExceptionCopyWith(_$BadRequestException value, - $Res Function(_$BadRequestException) then) = - __$$BadRequestExceptionCopyWithImpl<$Res>; +abstract class _$$BadRequestExceptionCopyWith { + factory _$$BadRequestExceptionCopyWith(_$BadRequestException value, + $Res Function(_$BadRequestException) then) = + __$$BadRequestExceptionCopyWithImpl; @useResult - $Res call({List? messages, int statusCode}); + $Res call({T flow}); } /// @nodoc -class __$$BadRequestExceptionCopyWithImpl<$Res> - extends _$CustomExceptionCopyWithImpl<$Res, _$BadRequestException> - implements _$$BadRequestExceptionCopyWith<$Res> { - __$$BadRequestExceptionCopyWithImpl( - _$BadRequestException _value, $Res Function(_$BadRequestException) _then) +class __$$BadRequestExceptionCopyWithImpl + extends _$CustomExceptionCopyWithImpl> + implements _$$BadRequestExceptionCopyWith { + __$$BadRequestExceptionCopyWithImpl(_$BadRequestException _value, + $Res Function(_$BadRequestException) _then) : super(_value, _then); @pragma('vm:prefer-inline') @override $Res call({ - Object? messages = freezed, - Object? statusCode = null, - }) { - return _then(_$BadRequestException( - messages: freezed == messages - ? _value._messages - : messages // ignore: cast_nullable_to_non_nullable - as List?, - statusCode: null == statusCode - ? _value.statusCode - : statusCode // ignore: cast_nullable_to_non_nullable - as int, + Object? flow = freezed, + }) { + return _then(_$BadRequestException( + flow: freezed == flow + ? _value.flow + : flow // ignore: cast_nullable_to_non_nullable + as T, )); } } /// @nodoc -class _$BadRequestException extends BadRequestException +class _$BadRequestException extends BadRequestException with DiagnosticableTreeMixin { - const _$BadRequestException( - {final List? messages, this.statusCode = 400}) - : _messages = messages, - super._(); - - final List? _messages; - @override - List? get messages { - final value = _messages; - if (value == null) return null; - if (_messages is EqualUnmodifiableListView) return _messages; - // ignore: implicit_dynamic_type - return EqualUnmodifiableListView(value); - } + const _$BadRequestException({required this.flow}) : super._(); @override - @JsonKey() - final int statusCode; + final T flow; @override String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) { - return 'CustomException.badRequest(messages: $messages, statusCode: $statusCode)'; + return 'CustomException<$T>.badRequest(flow: $flow)'; } @override void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); properties - ..add(DiagnosticsProperty('type', 'CustomException.badRequest')) - ..add(DiagnosticsProperty('messages', messages)) - ..add(DiagnosticsProperty('statusCode', statusCode)); + ..add(DiagnosticsProperty('type', 'CustomException<$T>.badRequest')) + ..add(DiagnosticsProperty('flow', flow)); } @override bool operator ==(dynamic other) { return identical(this, other) || (other.runtimeType == runtimeType && - other is _$BadRequestException && - const DeepCollectionEquality().equals(other._messages, _messages) && - (identical(other.statusCode, statusCode) || - other.statusCode == statusCode)); + other is _$BadRequestException && + const DeepCollectionEquality().equals(other.flow, flow)); } @override - int get hashCode => Object.hash( - runtimeType, const DeepCollectionEquality().hash(_messages), statusCode); + int get hashCode => + Object.hash(runtimeType, const DeepCollectionEquality().hash(flow)); @JsonKey(ignore: true) @override @pragma('vm:prefer-inline') - _$$BadRequestExceptionCopyWith<_$BadRequestException> get copyWith => - __$$BadRequestExceptionCopyWithImpl<_$BadRequestException>( + _$$BadRequestExceptionCopyWith> get copyWith => + __$$BadRequestExceptionCopyWithImpl>( this, _$identity); @override @optionalTypeArgs TResult when({ - required TResult Function(List? messages, int statusCode) - badRequest, - required TResult Function(int statusCode) unauthorized, - required TResult Function(int statusCode, String flowId, String? message) - flowExpired, + required TResult Function(T flow) badRequest, + required TResult Function() unauthorized, + required TResult Function(String flowId, String? message) flowExpired, + required TResult Function(Session? session) twoFactorAuthRequired, required TResult Function(String? message) unknown, }) { - return badRequest(messages, statusCode); + return badRequest(flow); } @override @optionalTypeArgs TResult? whenOrNull({ - TResult? Function(List? messages, int statusCode)? badRequest, - TResult? Function(int statusCode)? unauthorized, - TResult? Function(int statusCode, String flowId, String? message)? - flowExpired, + TResult? Function(T flow)? badRequest, + TResult? Function()? unauthorized, + TResult? Function(String flowId, String? message)? flowExpired, + TResult? Function(Session? session)? twoFactorAuthRequired, TResult? Function(String? message)? unknown, }) { - return badRequest?.call(messages, statusCode); + return badRequest?.call(flow); } @override @optionalTypeArgs TResult maybeWhen({ - TResult Function(List? messages, int statusCode)? badRequest, - TResult Function(int statusCode)? unauthorized, - TResult Function(int statusCode, String flowId, String? message)? - flowExpired, + TResult Function(T flow)? badRequest, + TResult Function()? unauthorized, + TResult Function(String flowId, String? message)? flowExpired, + TResult Function(Session? session)? twoFactorAuthRequired, TResult Function(String? message)? unknown, required TResult orElse(), }) { if (badRequest != null) { - return badRequest(messages, statusCode); + return badRequest(flow); } return orElse(); } @@ -231,10 +210,12 @@ class _$BadRequestException extends BadRequestException @override @optionalTypeArgs TResult map({ - required TResult Function(BadRequestException value) badRequest, - required TResult Function(UnauthorizedException value) unauthorized, - required TResult Function(FlowExpiredException value) flowExpired, - required TResult Function(UnknownException value) unknown, + required TResult Function(BadRequestException value) badRequest, + required TResult Function(UnauthorizedException value) unauthorized, + required TResult Function(FlowExpiredException value) flowExpired, + required TResult Function(TwoFactorAuthRequiredException value) + twoFactorAuthRequired, + required TResult Function(UnknownException value) unknown, }) { return badRequest(this); } @@ -242,10 +223,12 @@ class _$BadRequestException extends BadRequestException @override @optionalTypeArgs TResult? mapOrNull({ - TResult? Function(BadRequestException value)? badRequest, - TResult? Function(UnauthorizedException value)? unauthorized, - TResult? Function(FlowExpiredException value)? flowExpired, - TResult? Function(UnknownException value)? unknown, + TResult? Function(BadRequestException value)? badRequest, + TResult? Function(UnauthorizedException value)? unauthorized, + TResult? Function(FlowExpiredException value)? flowExpired, + TResult? Function(TwoFactorAuthRequiredException value)? + twoFactorAuthRequired, + TResult? Function(UnknownException value)? unknown, }) { return badRequest?.call(this); } @@ -253,10 +236,12 @@ class _$BadRequestException extends BadRequestException @override @optionalTypeArgs TResult maybeMap({ - TResult Function(BadRequestException value)? badRequest, - TResult Function(UnauthorizedException value)? unauthorized, - TResult Function(FlowExpiredException value)? flowExpired, - TResult Function(UnknownException value)? unknown, + TResult Function(BadRequestException value)? badRequest, + TResult Function(UnauthorizedException value)? unauthorized, + TResult Function(FlowExpiredException value)? flowExpired, + TResult Function(TwoFactorAuthRequiredException value)? + twoFactorAuthRequired, + TResult Function(UnknownException value)? unknown, required TResult orElse(), }) { if (badRequest != null) { @@ -266,129 +251,97 @@ class _$BadRequestException extends BadRequestException } } -abstract class BadRequestException extends CustomException { - const factory BadRequestException( - {final List? messages, - final int statusCode}) = _$BadRequestException; +abstract class BadRequestException extends CustomException { + const factory BadRequestException({required final T flow}) = + _$BadRequestException; const BadRequestException._() : super._(); - List? get messages; - int get statusCode; + T get flow; @JsonKey(ignore: true) - _$$BadRequestExceptionCopyWith<_$BadRequestException> get copyWith => + _$$BadRequestExceptionCopyWith> get copyWith => throw _privateConstructorUsedError; } /// @nodoc -abstract class _$$UnauthorizedExceptionCopyWith<$Res> { - factory _$$UnauthorizedExceptionCopyWith(_$UnauthorizedException value, - $Res Function(_$UnauthorizedException) then) = - __$$UnauthorizedExceptionCopyWithImpl<$Res>; - @useResult - $Res call({int statusCode}); +abstract class _$$UnauthorizedExceptionCopyWith { + factory _$$UnauthorizedExceptionCopyWith(_$UnauthorizedException value, + $Res Function(_$UnauthorizedException) then) = + __$$UnauthorizedExceptionCopyWithImpl; } /// @nodoc -class __$$UnauthorizedExceptionCopyWithImpl<$Res> - extends _$CustomExceptionCopyWithImpl<$Res, _$UnauthorizedException> - implements _$$UnauthorizedExceptionCopyWith<$Res> { - __$$UnauthorizedExceptionCopyWithImpl(_$UnauthorizedException _value, - $Res Function(_$UnauthorizedException) _then) +class __$$UnauthorizedExceptionCopyWithImpl + extends _$CustomExceptionCopyWithImpl> + implements _$$UnauthorizedExceptionCopyWith { + __$$UnauthorizedExceptionCopyWithImpl(_$UnauthorizedException _value, + $Res Function(_$UnauthorizedException) _then) : super(_value, _then); - - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? statusCode = null, - }) { - return _then(_$UnauthorizedException( - statusCode: null == statusCode - ? _value.statusCode - : statusCode // ignore: cast_nullable_to_non_nullable - as int, - )); - } } /// @nodoc -class _$UnauthorizedException extends UnauthorizedException +class _$UnauthorizedException extends UnauthorizedException with DiagnosticableTreeMixin { - const _$UnauthorizedException({this.statusCode = 401}) : super._(); - - @override - @JsonKey() - final int statusCode; + const _$UnauthorizedException() : super._(); @override String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) { - return 'CustomException.unauthorized(statusCode: $statusCode)'; + return 'CustomException<$T>.unauthorized()'; } @override void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); properties - ..add(DiagnosticsProperty('type', 'CustomException.unauthorized')) - ..add(DiagnosticsProperty('statusCode', statusCode)); + .add(DiagnosticsProperty('type', 'CustomException<$T>.unauthorized')); } @override bool operator ==(dynamic other) { return identical(this, other) || (other.runtimeType == runtimeType && - other is _$UnauthorizedException && - (identical(other.statusCode, statusCode) || - other.statusCode == statusCode)); + other is _$UnauthorizedException); } @override - int get hashCode => Object.hash(runtimeType, statusCode); - - @JsonKey(ignore: true) - @override - @pragma('vm:prefer-inline') - _$$UnauthorizedExceptionCopyWith<_$UnauthorizedException> get copyWith => - __$$UnauthorizedExceptionCopyWithImpl<_$UnauthorizedException>( - this, _$identity); + int get hashCode => runtimeType.hashCode; @override @optionalTypeArgs TResult when({ - required TResult Function(List? messages, int statusCode) - badRequest, - required TResult Function(int statusCode) unauthorized, - required TResult Function(int statusCode, String flowId, String? message) - flowExpired, + required TResult Function(T flow) badRequest, + required TResult Function() unauthorized, + required TResult Function(String flowId, String? message) flowExpired, + required TResult Function(Session? session) twoFactorAuthRequired, required TResult Function(String? message) unknown, }) { - return unauthorized(statusCode); + return unauthorized(); } @override @optionalTypeArgs TResult? whenOrNull({ - TResult? Function(List? messages, int statusCode)? badRequest, - TResult? Function(int statusCode)? unauthorized, - TResult? Function(int statusCode, String flowId, String? message)? - flowExpired, + TResult? Function(T flow)? badRequest, + TResult? Function()? unauthorized, + TResult? Function(String flowId, String? message)? flowExpired, + TResult? Function(Session? session)? twoFactorAuthRequired, TResult? Function(String? message)? unknown, }) { - return unauthorized?.call(statusCode); + return unauthorized?.call(); } @override @optionalTypeArgs TResult maybeWhen({ - TResult Function(List? messages, int statusCode)? badRequest, - TResult Function(int statusCode)? unauthorized, - TResult Function(int statusCode, String flowId, String? message)? - flowExpired, + TResult Function(T flow)? badRequest, + TResult Function()? unauthorized, + TResult Function(String flowId, String? message)? flowExpired, + TResult Function(Session? session)? twoFactorAuthRequired, TResult Function(String? message)? unknown, required TResult orElse(), }) { if (unauthorized != null) { - return unauthorized(statusCode); + return unauthorized(); } return orElse(); } @@ -396,10 +349,12 @@ class _$UnauthorizedException extends UnauthorizedException @override @optionalTypeArgs TResult map({ - required TResult Function(BadRequestException value) badRequest, - required TResult Function(UnauthorizedException value) unauthorized, - required TResult Function(FlowExpiredException value) flowExpired, - required TResult Function(UnknownException value) unknown, + required TResult Function(BadRequestException value) badRequest, + required TResult Function(UnauthorizedException value) unauthorized, + required TResult Function(FlowExpiredException value) flowExpired, + required TResult Function(TwoFactorAuthRequiredException value) + twoFactorAuthRequired, + required TResult Function(UnknownException value) unknown, }) { return unauthorized(this); } @@ -407,10 +362,12 @@ class _$UnauthorizedException extends UnauthorizedException @override @optionalTypeArgs TResult? mapOrNull({ - TResult? Function(BadRequestException value)? badRequest, - TResult? Function(UnauthorizedException value)? unauthorized, - TResult? Function(FlowExpiredException value)? flowExpired, - TResult? Function(UnknownException value)? unknown, + TResult? Function(BadRequestException value)? badRequest, + TResult? Function(UnauthorizedException value)? unauthorized, + TResult? Function(FlowExpiredException value)? flowExpired, + TResult? Function(TwoFactorAuthRequiredException value)? + twoFactorAuthRequired, + TResult? Function(UnknownException value)? unknown, }) { return unauthorized?.call(this); } @@ -418,10 +375,12 @@ class _$UnauthorizedException extends UnauthorizedException @override @optionalTypeArgs TResult maybeMap({ - TResult Function(BadRequestException value)? badRequest, - TResult Function(UnauthorizedException value)? unauthorized, - TResult Function(FlowExpiredException value)? flowExpired, - TResult Function(UnknownException value)? unknown, + TResult Function(BadRequestException value)? badRequest, + TResult Function(UnauthorizedException value)? unauthorized, + TResult Function(FlowExpiredException value)? flowExpired, + TResult Function(TwoFactorAuthRequiredException value)? + twoFactorAuthRequired, + TResult Function(UnknownException value)? unknown, required TResult orElse(), }) { if (unauthorized != null) { @@ -431,46 +390,35 @@ class _$UnauthorizedException extends UnauthorizedException } } -abstract class UnauthorizedException extends CustomException { - const factory UnauthorizedException({final int statusCode}) = - _$UnauthorizedException; +abstract class UnauthorizedException extends CustomException { + const factory UnauthorizedException() = _$UnauthorizedException; const UnauthorizedException._() : super._(); - - int get statusCode; - @JsonKey(ignore: true) - _$$UnauthorizedExceptionCopyWith<_$UnauthorizedException> get copyWith => - throw _privateConstructorUsedError; } /// @nodoc -abstract class _$$FlowExpiredExceptionCopyWith<$Res> { - factory _$$FlowExpiredExceptionCopyWith(_$FlowExpiredException value, - $Res Function(_$FlowExpiredException) then) = - __$$FlowExpiredExceptionCopyWithImpl<$Res>; +abstract class _$$FlowExpiredExceptionCopyWith { + factory _$$FlowExpiredExceptionCopyWith(_$FlowExpiredException value, + $Res Function(_$FlowExpiredException) then) = + __$$FlowExpiredExceptionCopyWithImpl; @useResult - $Res call({int statusCode, String flowId, String? message}); + $Res call({String flowId, String? message}); } /// @nodoc -class __$$FlowExpiredExceptionCopyWithImpl<$Res> - extends _$CustomExceptionCopyWithImpl<$Res, _$FlowExpiredException> - implements _$$FlowExpiredExceptionCopyWith<$Res> { - __$$FlowExpiredExceptionCopyWithImpl(_$FlowExpiredException _value, - $Res Function(_$FlowExpiredException) _then) +class __$$FlowExpiredExceptionCopyWithImpl + extends _$CustomExceptionCopyWithImpl> + implements _$$FlowExpiredExceptionCopyWith { + __$$FlowExpiredExceptionCopyWithImpl(_$FlowExpiredException _value, + $Res Function(_$FlowExpiredException) _then) : super(_value, _then); @pragma('vm:prefer-inline') @override $Res call({ - Object? statusCode = null, Object? flowId = null, Object? message = freezed, }) { - return _then(_$FlowExpiredException( - statusCode: null == statusCode - ? _value.statusCode - : statusCode // ignore: cast_nullable_to_non_nullable - as int, + return _then(_$FlowExpiredException( flowId: null == flowId ? _value.flowId : flowId // ignore: cast_nullable_to_non_nullable @@ -485,15 +433,11 @@ class __$$FlowExpiredExceptionCopyWithImpl<$Res> /// @nodoc -class _$FlowExpiredException extends FlowExpiredException +class _$FlowExpiredException extends FlowExpiredException with DiagnosticableTreeMixin { - const _$FlowExpiredException( - {this.statusCode = 410, required this.flowId, this.message}) + const _$FlowExpiredException({required this.flowId, this.message}) : super._(); - @override - @JsonKey() - final int statusCode; @override final String flowId; @override @@ -501,15 +445,14 @@ class _$FlowExpiredException extends FlowExpiredException @override String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) { - return 'CustomException.flowExpired(statusCode: $statusCode, flowId: $flowId, message: $message)'; + return 'CustomException<$T>.flowExpired(flowId: $flowId, message: $message)'; } @override void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); properties - ..add(DiagnosticsProperty('type', 'CustomException.flowExpired')) - ..add(DiagnosticsProperty('statusCode', statusCode)) + ..add(DiagnosticsProperty('type', 'CustomException<$T>.flowExpired')) ..add(DiagnosticsProperty('flowId', flowId)) ..add(DiagnosticsProperty('message', message)); } @@ -518,60 +461,57 @@ class _$FlowExpiredException extends FlowExpiredException bool operator ==(dynamic other) { return identical(this, other) || (other.runtimeType == runtimeType && - other is _$FlowExpiredException && - (identical(other.statusCode, statusCode) || - other.statusCode == statusCode) && + other is _$FlowExpiredException && (identical(other.flowId, flowId) || other.flowId == flowId) && (identical(other.message, message) || other.message == message)); } @override - int get hashCode => Object.hash(runtimeType, statusCode, flowId, message); + int get hashCode => Object.hash(runtimeType, flowId, message); @JsonKey(ignore: true) @override @pragma('vm:prefer-inline') - _$$FlowExpiredExceptionCopyWith<_$FlowExpiredException> get copyWith => - __$$FlowExpiredExceptionCopyWithImpl<_$FlowExpiredException>( + _$$FlowExpiredExceptionCopyWith> get copyWith => + __$$FlowExpiredExceptionCopyWithImpl>( this, _$identity); @override @optionalTypeArgs TResult when({ - required TResult Function(List? messages, int statusCode) - badRequest, - required TResult Function(int statusCode) unauthorized, - required TResult Function(int statusCode, String flowId, String? message) - flowExpired, + required TResult Function(T flow) badRequest, + required TResult Function() unauthorized, + required TResult Function(String flowId, String? message) flowExpired, + required TResult Function(Session? session) twoFactorAuthRequired, required TResult Function(String? message) unknown, }) { - return flowExpired(statusCode, flowId, message); + return flowExpired(flowId, message); } @override @optionalTypeArgs TResult? whenOrNull({ - TResult? Function(List? messages, int statusCode)? badRequest, - TResult? Function(int statusCode)? unauthorized, - TResult? Function(int statusCode, String flowId, String? message)? - flowExpired, + TResult? Function(T flow)? badRequest, + TResult? Function()? unauthorized, + TResult? Function(String flowId, String? message)? flowExpired, + TResult? Function(Session? session)? twoFactorAuthRequired, TResult? Function(String? message)? unknown, }) { - return flowExpired?.call(statusCode, flowId, message); + return flowExpired?.call(flowId, message); } @override @optionalTypeArgs TResult maybeWhen({ - TResult Function(List? messages, int statusCode)? badRequest, - TResult Function(int statusCode)? unauthorized, - TResult Function(int statusCode, String flowId, String? message)? - flowExpired, + TResult Function(T flow)? badRequest, + TResult Function()? unauthorized, + TResult Function(String flowId, String? message)? flowExpired, + TResult Function(Session? session)? twoFactorAuthRequired, TResult Function(String? message)? unknown, required TResult orElse(), }) { if (flowExpired != null) { - return flowExpired(statusCode, flowId, message); + return flowExpired(flowId, message); } return orElse(); } @@ -579,10 +519,12 @@ class _$FlowExpiredException extends FlowExpiredException @override @optionalTypeArgs TResult map({ - required TResult Function(BadRequestException value) badRequest, - required TResult Function(UnauthorizedException value) unauthorized, - required TResult Function(FlowExpiredException value) flowExpired, - required TResult Function(UnknownException value) unknown, + required TResult Function(BadRequestException value) badRequest, + required TResult Function(UnauthorizedException value) unauthorized, + required TResult Function(FlowExpiredException value) flowExpired, + required TResult Function(TwoFactorAuthRequiredException value) + twoFactorAuthRequired, + required TResult Function(UnknownException value) unknown, }) { return flowExpired(this); } @@ -590,10 +532,12 @@ class _$FlowExpiredException extends FlowExpiredException @override @optionalTypeArgs TResult? mapOrNull({ - TResult? Function(BadRequestException value)? badRequest, - TResult? Function(UnauthorizedException value)? unauthorized, - TResult? Function(FlowExpiredException value)? flowExpired, - TResult? Function(UnknownException value)? unknown, + TResult? Function(BadRequestException value)? badRequest, + TResult? Function(UnauthorizedException value)? unauthorized, + TResult? Function(FlowExpiredException value)? flowExpired, + TResult? Function(TwoFactorAuthRequiredException value)? + twoFactorAuthRequired, + TResult? Function(UnknownException value)? unknown, }) { return flowExpired?.call(this); } @@ -601,10 +545,12 @@ class _$FlowExpiredException extends FlowExpiredException @override @optionalTypeArgs TResult maybeMap({ - TResult Function(BadRequestException value)? badRequest, - TResult Function(UnauthorizedException value)? unauthorized, - TResult Function(FlowExpiredException value)? flowExpired, - TResult Function(UnknownException value)? unknown, + TResult Function(BadRequestException value)? badRequest, + TResult Function(UnauthorizedException value)? unauthorized, + TResult Function(FlowExpiredException value)? flowExpired, + TResult Function(TwoFactorAuthRequiredException value)? + twoFactorAuthRequired, + TResult Function(UnknownException value)? unknown, required TResult orElse(), }) { if (flowExpired != null) { @@ -614,36 +560,206 @@ class _$FlowExpiredException extends FlowExpiredException } } -abstract class FlowExpiredException extends CustomException { +abstract class FlowExpiredException extends CustomException { const factory FlowExpiredException( - {final int statusCode, - required final String flowId, - final String? message}) = _$FlowExpiredException; + {required final String flowId, + final String? message}) = _$FlowExpiredException; const FlowExpiredException._() : super._(); - int get statusCode; String get flowId; String? get message; @JsonKey(ignore: true) - _$$FlowExpiredExceptionCopyWith<_$FlowExpiredException> get copyWith => + _$$FlowExpiredExceptionCopyWith> get copyWith => throw _privateConstructorUsedError; } /// @nodoc -abstract class _$$UnknownExceptionCopyWith<$Res> { - factory _$$UnknownExceptionCopyWith( - _$UnknownException value, $Res Function(_$UnknownException) then) = - __$$UnknownExceptionCopyWithImpl<$Res>; +abstract class _$$TwoFactorAuthRequiredExceptionCopyWith { + factory _$$TwoFactorAuthRequiredExceptionCopyWith( + _$TwoFactorAuthRequiredException value, + $Res Function(_$TwoFactorAuthRequiredException) then) = + __$$TwoFactorAuthRequiredExceptionCopyWithImpl; + @useResult + $Res call({Session? session}); +} + +/// @nodoc +class __$$TwoFactorAuthRequiredExceptionCopyWithImpl + extends _$CustomExceptionCopyWithImpl> + implements _$$TwoFactorAuthRequiredExceptionCopyWith { + __$$TwoFactorAuthRequiredExceptionCopyWithImpl( + _$TwoFactorAuthRequiredException _value, + $Res Function(_$TwoFactorAuthRequiredException) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? session = freezed, + }) { + return _then(_$TwoFactorAuthRequiredException( + session: freezed == session + ? _value.session + : session // ignore: cast_nullable_to_non_nullable + as Session?, + )); + } +} + +/// @nodoc + +class _$TwoFactorAuthRequiredException + extends TwoFactorAuthRequiredException with DiagnosticableTreeMixin { + const _$TwoFactorAuthRequiredException({this.session}) : super._(); + + @override + final Session? session; + + @override + String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) { + return 'CustomException<$T>.twoFactorAuthRequired(session: $session)'; + } + + @override + void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); + properties + ..add(DiagnosticsProperty( + 'type', 'CustomException<$T>.twoFactorAuthRequired')) + ..add(DiagnosticsProperty('session', session)); + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$TwoFactorAuthRequiredException && + (identical(other.session, session) || other.session == session)); + } + + @override + int get hashCode => Object.hash(runtimeType, session); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$TwoFactorAuthRequiredExceptionCopyWith> + get copyWith => __$$TwoFactorAuthRequiredExceptionCopyWithImpl>(this, _$identity); + + @override + @optionalTypeArgs + TResult when({ + required TResult Function(T flow) badRequest, + required TResult Function() unauthorized, + required TResult Function(String flowId, String? message) flowExpired, + required TResult Function(Session? session) twoFactorAuthRequired, + required TResult Function(String? message) unknown, + }) { + return twoFactorAuthRequired(session); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function(T flow)? badRequest, + TResult? Function()? unauthorized, + TResult? Function(String flowId, String? message)? flowExpired, + TResult? Function(Session? session)? twoFactorAuthRequired, + TResult? Function(String? message)? unknown, + }) { + return twoFactorAuthRequired?.call(session); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function(T flow)? badRequest, + TResult Function()? unauthorized, + TResult Function(String flowId, String? message)? flowExpired, + TResult Function(Session? session)? twoFactorAuthRequired, + TResult Function(String? message)? unknown, + required TResult orElse(), + }) { + if (twoFactorAuthRequired != null) { + return twoFactorAuthRequired(session); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(BadRequestException value) badRequest, + required TResult Function(UnauthorizedException value) unauthorized, + required TResult Function(FlowExpiredException value) flowExpired, + required TResult Function(TwoFactorAuthRequiredException value) + twoFactorAuthRequired, + required TResult Function(UnknownException value) unknown, + }) { + return twoFactorAuthRequired(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(BadRequestException value)? badRequest, + TResult? Function(UnauthorizedException value)? unauthorized, + TResult? Function(FlowExpiredException value)? flowExpired, + TResult? Function(TwoFactorAuthRequiredException value)? + twoFactorAuthRequired, + TResult? Function(UnknownException value)? unknown, + }) { + return twoFactorAuthRequired?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(BadRequestException value)? badRequest, + TResult Function(UnauthorizedException value)? unauthorized, + TResult Function(FlowExpiredException value)? flowExpired, + TResult Function(TwoFactorAuthRequiredException value)? + twoFactorAuthRequired, + TResult Function(UnknownException value)? unknown, + required TResult orElse(), + }) { + if (twoFactorAuthRequired != null) { + return twoFactorAuthRequired(this); + } + return orElse(); + } +} + +abstract class TwoFactorAuthRequiredException extends CustomException { + const factory TwoFactorAuthRequiredException({final Session? session}) = + _$TwoFactorAuthRequiredException; + const TwoFactorAuthRequiredException._() : super._(); + + Session? get session; + @JsonKey(ignore: true) + _$$TwoFactorAuthRequiredExceptionCopyWith> + get copyWith => throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class _$$UnknownExceptionCopyWith { + factory _$$UnknownExceptionCopyWith(_$UnknownException value, + $Res Function(_$UnknownException) then) = + __$$UnknownExceptionCopyWithImpl; @useResult $Res call({String? message}); } /// @nodoc -class __$$UnknownExceptionCopyWithImpl<$Res> - extends _$CustomExceptionCopyWithImpl<$Res, _$UnknownException> - implements _$$UnknownExceptionCopyWith<$Res> { +class __$$UnknownExceptionCopyWithImpl + extends _$CustomExceptionCopyWithImpl> + implements _$$UnknownExceptionCopyWith { __$$UnknownExceptionCopyWithImpl( - _$UnknownException _value, $Res Function(_$UnknownException) _then) + _$UnknownException _value, $Res Function(_$UnknownException) _then) : super(_value, _then); @pragma('vm:prefer-inline') @@ -651,7 +767,7 @@ class __$$UnknownExceptionCopyWithImpl<$Res> $Res call({ Object? message = freezed, }) { - return _then(_$UnknownException( + return _then(_$UnknownException( message: freezed == message ? _value.message : message // ignore: cast_nullable_to_non_nullable @@ -662,7 +778,8 @@ class __$$UnknownExceptionCopyWithImpl<$Res> /// @nodoc -class _$UnknownException extends UnknownException with DiagnosticableTreeMixin { +class _$UnknownException extends UnknownException + with DiagnosticableTreeMixin { const _$UnknownException( {this.message = 'An error occured. Please try again later.'}) : super._(); @@ -673,14 +790,14 @@ class _$UnknownException extends UnknownException with DiagnosticableTreeMixin { @override String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) { - return 'CustomException.unknown(message: $message)'; + return 'CustomException<$T>.unknown(message: $message)'; } @override void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); properties - ..add(DiagnosticsProperty('type', 'CustomException.unknown')) + ..add(DiagnosticsProperty('type', 'CustomException<$T>.unknown')) ..add(DiagnosticsProperty('message', message)); } @@ -688,7 +805,7 @@ class _$UnknownException extends UnknownException with DiagnosticableTreeMixin { bool operator ==(dynamic other) { return identical(this, other) || (other.runtimeType == runtimeType && - other is _$UnknownException && + other is _$UnknownException && (identical(other.message, message) || other.message == message)); } @@ -698,17 +815,17 @@ class _$UnknownException extends UnknownException with DiagnosticableTreeMixin { @JsonKey(ignore: true) @override @pragma('vm:prefer-inline') - _$$UnknownExceptionCopyWith<_$UnknownException> get copyWith => - __$$UnknownExceptionCopyWithImpl<_$UnknownException>(this, _$identity); + _$$UnknownExceptionCopyWith> get copyWith => + __$$UnknownExceptionCopyWithImpl>( + this, _$identity); @override @optionalTypeArgs TResult when({ - required TResult Function(List? messages, int statusCode) - badRequest, - required TResult Function(int statusCode) unauthorized, - required TResult Function(int statusCode, String flowId, String? message) - flowExpired, + required TResult Function(T flow) badRequest, + required TResult Function() unauthorized, + required TResult Function(String flowId, String? message) flowExpired, + required TResult Function(Session? session) twoFactorAuthRequired, required TResult Function(String? message) unknown, }) { return unknown(message); @@ -717,10 +834,10 @@ class _$UnknownException extends UnknownException with DiagnosticableTreeMixin { @override @optionalTypeArgs TResult? whenOrNull({ - TResult? Function(List? messages, int statusCode)? badRequest, - TResult? Function(int statusCode)? unauthorized, - TResult? Function(int statusCode, String flowId, String? message)? - flowExpired, + TResult? Function(T flow)? badRequest, + TResult? Function()? unauthorized, + TResult? Function(String flowId, String? message)? flowExpired, + TResult? Function(Session? session)? twoFactorAuthRequired, TResult? Function(String? message)? unknown, }) { return unknown?.call(message); @@ -729,10 +846,10 @@ class _$UnknownException extends UnknownException with DiagnosticableTreeMixin { @override @optionalTypeArgs TResult maybeWhen({ - TResult Function(List? messages, int statusCode)? badRequest, - TResult Function(int statusCode)? unauthorized, - TResult Function(int statusCode, String flowId, String? message)? - flowExpired, + TResult Function(T flow)? badRequest, + TResult Function()? unauthorized, + TResult Function(String flowId, String? message)? flowExpired, + TResult Function(Session? session)? twoFactorAuthRequired, TResult Function(String? message)? unknown, required TResult orElse(), }) { @@ -745,10 +862,12 @@ class _$UnknownException extends UnknownException with DiagnosticableTreeMixin { @override @optionalTypeArgs TResult map({ - required TResult Function(BadRequestException value) badRequest, - required TResult Function(UnauthorizedException value) unauthorized, - required TResult Function(FlowExpiredException value) flowExpired, - required TResult Function(UnknownException value) unknown, + required TResult Function(BadRequestException value) badRequest, + required TResult Function(UnauthorizedException value) unauthorized, + required TResult Function(FlowExpiredException value) flowExpired, + required TResult Function(TwoFactorAuthRequiredException value) + twoFactorAuthRequired, + required TResult Function(UnknownException value) unknown, }) { return unknown(this); } @@ -756,10 +875,12 @@ class _$UnknownException extends UnknownException with DiagnosticableTreeMixin { @override @optionalTypeArgs TResult? mapOrNull({ - TResult? Function(BadRequestException value)? badRequest, - TResult? Function(UnauthorizedException value)? unauthorized, - TResult? Function(FlowExpiredException value)? flowExpired, - TResult? Function(UnknownException value)? unknown, + TResult? Function(BadRequestException value)? badRequest, + TResult? Function(UnauthorizedException value)? unauthorized, + TResult? Function(FlowExpiredException value)? flowExpired, + TResult? Function(TwoFactorAuthRequiredException value)? + twoFactorAuthRequired, + TResult? Function(UnknownException value)? unknown, }) { return unknown?.call(this); } @@ -767,10 +888,12 @@ class _$UnknownException extends UnknownException with DiagnosticableTreeMixin { @override @optionalTypeArgs TResult maybeMap({ - TResult Function(BadRequestException value)? badRequest, - TResult Function(UnauthorizedException value)? unauthorized, - TResult Function(FlowExpiredException value)? flowExpired, - TResult Function(UnknownException value)? unknown, + TResult Function(BadRequestException value)? badRequest, + TResult Function(UnauthorizedException value)? unauthorized, + TResult Function(FlowExpiredException value)? flowExpired, + TResult Function(TwoFactorAuthRequiredException value)? + twoFactorAuthRequired, + TResult Function(UnknownException value)? unknown, required TResult orElse(), }) { if (unknown != null) { @@ -780,12 +903,13 @@ class _$UnknownException extends UnknownException with DiagnosticableTreeMixin { } } -abstract class UnknownException extends CustomException { - const factory UnknownException({final String? message}) = _$UnknownException; +abstract class UnknownException extends CustomException { + const factory UnknownException({final String? message}) = + _$UnknownException; const UnknownException._() : super._(); String? get message; @JsonKey(ignore: true) - _$$UnknownExceptionCopyWith<_$UnknownException> get copyWith => + _$$UnknownExceptionCopyWith> get copyWith => throw _privateConstructorUsedError; } diff --git a/flutter-ory-network/lib/widgets/helpers.dart b/flutter-ory-network/lib/widgets/helpers.dart new file mode 100644 index 0000000..b565ae7 --- /dev/null +++ b/flutter-ory-network/lib/widgets/helpers.dart @@ -0,0 +1,95 @@ +import 'package:bloc/bloc.dart'; +import 'package:flutter/material.dart'; +import 'package:ory_client/ory_client.dart'; + +import 'nodes/input.dart'; +import 'nodes/input_submit.dart'; +import 'nodes/text.dart'; + +getMessageColor(UiTextTypeEnum type) { + switch (type) { + case UiTextTypeEnum.success: + return Colors.green; + case UiTextTypeEnum.error: + return Colors.red; + case UiTextTypeEnum.info: + return Colors.grey; + } +} + +buildFlowNotCreated(BuildContext context, String? message) { + if (message != null) { + return Center( + child: Text( + message, + style: const TextStyle(color: Colors.red), + )); + } else { + return const Center(child: CircularProgressIndicator()); + } +} + +buildGroup( + BuildContext context, + UiNodeGroupEnum group, + List nodes, + void Function(BuildContext, String, String) onInputChange, + void Function(BuildContext, UiNodeGroupEnum, String, String) + onInputSubmit) { + final formKey = GlobalKey(); + return Padding( + padding: const EdgeInsets.only(bottom: 0), + child: Form( + key: formKey, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ListView.builder( + physics: const NeverScrollableScrollPhysics(), + shrinkWrap: true, + itemBuilder: ((BuildContext context, index) { + final attributes = nodes[index].attributes.oneOf; + if (attributes.isType(UiNodeInputAttributes)) { + return buildInputNode(context, formKey, nodes[index], + onInputChange, onInputSubmit); + } else if (attributes.isType(UiNodeTextAttributes)) { + return TextNode(node: nodes[index]); + } else { + return Container(); + } + }), + itemCount: nodes.length), + ], + ), + ), + ); +} + +buildInputNode( + BuildContext context, + GlobalKey formKey, + UiNode node, + void Function(BuildContext, String, String) onInputChange, + void Function(BuildContext, UiNodeGroupEnum, String, String) + onInputSubmit) { + final inputNode = node.attributes.oneOf.value as UiNodeInputAttributes; + switch (inputNode.type) { + case UiNodeInputAttributesTypeEnum.submit: + return InputSubmitNode( + node: node, + formKey: formKey, + onChange: onInputChange, + onSubmit: onInputSubmit); + case UiNodeInputAttributesTypeEnum.button: + return InputSubmitNode( + node: node, + formKey: formKey, + onChange: onInputChange, + onSubmit: onInputSubmit); + case UiNodeInputAttributesTypeEnum.hidden: + return const SizedBox.shrink(); + + default: + return InputNode(node: node, onChange: onInputChange); + } +} diff --git a/flutter-ory-network/lib/widgets/nodes/input.dart b/flutter-ory-network/lib/widgets/nodes/input.dart new file mode 100644 index 0000000..3be9b23 --- /dev/null +++ b/flutter-ory-network/lib/widgets/nodes/input.dart @@ -0,0 +1,156 @@ +import 'package:collection/collection.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:ory_client/ory_client.dart'; +import 'package:ory_network_flutter/blocs/login/login_bloc.dart'; +import 'package:ory_network_flutter/blocs/registration/registration_bloc.dart'; + +import '../helpers.dart'; + +class InputNode extends StatefulWidget { + final UiNode node; + final void Function(BuildContext, String, String) onChange; + + const InputNode({super.key, required this.node, required this.onChange}); + @override + State createState() => _InputNodeState(); +} + +class _InputNodeState extends State { + TextEditingController textEditingController = TextEditingController(); + bool? isPasswordHidden; + + @override + void initState() { + super.initState(); + // assign node value to text controller on init + final attributes = + widget.node.attributes.oneOf.value as UiNodeInputAttributes; + // if this is a password field, hide the text + if (attributes.name == 'password') { + setState(() { + isPasswordHidden = true; + }); + } + final fieldValue = attributes.value; + textEditingController.text = fieldValue != null ? fieldValue.asString : ''; + } + +// get text input type of a specific node + _getTextInputType(UiNodeInputAttributesTypeEnum type) { + switch (type) { + case UiNodeInputAttributesTypeEnum.datetimeLocal: + return TextInputType.datetime; + case UiNodeInputAttributesTypeEnum.email: + return TextInputType.emailAddress; + case UiNodeInputAttributesTypeEnum.hidden: + return TextInputType.none; + case UiNodeInputAttributesTypeEnum.number: + return TextInputType.number; + case UiNodeInputAttributesTypeEnum.password: + return TextInputType.text; + case UiNodeInputAttributesTypeEnum.tel: + return TextInputType.phone; + case UiNodeInputAttributesTypeEnum.text: + return TextInputType.text; + case UiNodeInputAttributesTypeEnum.url: + return TextInputType.url; + default: + return null; + } + } + + @override + Widget build(BuildContext context) { + print(widget.node.attributes.oneOf.value); + final attributes = + widget.node.attributes.oneOf.value as UiNodeInputAttributes; + + return BlocListener( + bloc: (context).read(), + listener: (context, state) { + UiNode? node; + // find current node in updated state + if (state is LoginState) { + node = state.loginFlow?.ui.nodes.firstWhereOrNull((element) { + if (element.attributes.oneOf.isType(UiNodeInputAttributes)) { + return (element.attributes.oneOf.value as UiNodeInputAttributes) + .name == + attributes.name; + } else { + return false; + } + }); + } else if (state is RegistrationState) { + node = state.registrationFlow?.ui.nodes.firstWhereOrNull((element) { + if (element.attributes.oneOf.isType(UiNodeInputAttributes)) { + return (element.attributes.oneOf.value as UiNodeInputAttributes) + .name == + attributes.name; + } else { + return false; + } + }); + } + + // assign new value of node to text controller + textEditingController.text = + (node?.attributes.oneOf.value as UiNodeInputAttributes) + .value + ?.asString ?? + ''; + }, + child: Padding( + padding: const EdgeInsets.only(bottom: 20.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (widget.node.meta.label?.text != null) + Text( + widget.node.meta.label!.text, + style: const TextStyle(fontWeight: FontWeight.w600), + ), + const SizedBox( + height: 4, + ), + TextFormField( + controller: textEditingController, + enabled: !attributes.disabled, + keyboardType: _getTextInputType(attributes.type), + onChanged: (String value) { + widget.onChange(context, value, attributes.name); + }, + obscureText: isPasswordHidden ?? false, + decoration: isPasswordHidden == null + ? null + : InputDecoration( + suffixIcon: GestureDetector( + onTap: () => setState(() { + isPasswordHidden = !isPasswordHidden!; + }), + child: ImageIcon(isPasswordHidden! + ? const AssetImage('assets/icons/eye.png') + : const AssetImage('assets/icons/eye-off.png')), + )), + validator: (value) { + if (attributes.required_ != null) { + if (attributes.required_! && + (value == null || value.isEmpty)) { + return 'Property is required'; + } + } + return null; + }, + ), + for (var message in widget.node.messages) + Padding( + padding: const EdgeInsets.only(top: 20), + child: Text(message.text, + style: TextStyle(color: getMessageColor(message.type))), + ), + ], + ), + ), + ); + } +} diff --git a/flutter-ory-network/lib/widgets/nodes/input_submit.dart b/flutter-ory-network/lib/widgets/nodes/input_submit.dart new file mode 100644 index 0000000..542c9b6 --- /dev/null +++ b/flutter-ory-network/lib/widgets/nodes/input_submit.dart @@ -0,0 +1,53 @@ +import 'package:flutter/material.dart'; +import 'package:ory_client/ory_client.dart'; + +class InputSubmitNode extends StatelessWidget { + final GlobalKey formKey; + final UiNode node; + final void Function(BuildContext, String, String) onChange; + final void Function(BuildContext, UiNodeGroupEnum, String, String) onSubmit; + + const InputSubmitNode( + {super.key, + required this.formKey, + required this.node, + required this.onChange, + required this.onSubmit}); + + @override + Widget build(BuildContext context) { + return Column( + children: [ + SizedBox( + width: double.infinity, + child: FilledButton( + // validate input fields that belong to this buttons group + onPressed: () { + if (formKey.currentState!.validate()) { + final attributes = + node.attributes.oneOf.value as UiNodeInputAttributes; + final type = attributes.type; + // if attribute type is a button, set its value to true on submit + if (type == UiNodeInputAttributesTypeEnum.button || + type == UiNodeInputAttributesTypeEnum.submit && + attributes.value?.value == 'false') { + final nodeName = attributes.name; + + onChange(context, 'true', nodeName); + } + onSubmit( + context, + node.group, + attributes.name, + attributes.value!.isString + ? attributes.value!.asString + : ''); + } + }, + child: Text(node.meta.label?.text ?? ''), + ), + ), + ], + ); + } +} diff --git a/flutter-ory-network/lib/widgets/nodes/provider.dart b/flutter-ory-network/lib/widgets/nodes/provider.dart new file mode 100644 index 0000000..5c5ccc1 --- /dev/null +++ b/flutter-ory-network/lib/widgets/nodes/provider.dart @@ -0,0 +1,26 @@ +// Copyright © 2023 Ory Corp +// SPDX-License-Identifier: Apache-2.0 + +import 'package:flutter/material.dart'; +import 'package:ory_client/ory_client.dart'; + +class SocialProviderInput extends StatelessWidget { + final UiNode node; + + const SocialProviderInput({super.key, required this.node}); + @override + Widget build(BuildContext context) { + final provider = node.attributes.oneOf.isType(UiNodeInputAttributes) + ? (node.attributes.oneOf.value as UiNodeInputAttributes).value?.asString + : null; + return SizedBox( + width: double.infinity, + child: OutlinedButton.icon( + icon: Image.asset( + 'assets/images/flows-auth-buttons-social-$provider.png'), + label: Text(node.meta.label?.text ?? ''), + onPressed: () {}, + ), + ); + } +} diff --git a/flutter-ory-network/lib/widgets/nodes/text.dart b/flutter-ory-network/lib/widgets/nodes/text.dart new file mode 100644 index 0000000..26bd54f --- /dev/null +++ b/flutter-ory-network/lib/widgets/nodes/text.dart @@ -0,0 +1,25 @@ +import 'package:flutter/material.dart'; +import 'package:ory_client/ory_client.dart'; + +class TextNode extends StatelessWidget { + final UiNode node; + + const TextNode({super.key, required this.node}); + @override + Widget build(BuildContext context) { + final attributes = node.attributes.oneOf.value as UiNodeTextAttributes; + return Padding( + padding: const EdgeInsets.only(bottom: 20.0), + child: Column( + children: [ + // show label test if it is available + if (node.meta.label?.text != null) Text(node.meta.label!.text), + const SizedBox( + height: 10, + ), + Text(attributes.text.text), + ], + ), + ); + } +} diff --git a/flutter-ory-network/pubspec.lock b/flutter-ory-network/pubspec.lock index 642803c..9ce2d1f 100644 --- a/flutter-ory-network/pubspec.lock +++ b/flutter-ory-network/pubspec.lock @@ -98,7 +98,7 @@ packages: source: hosted version: "7.2.10" built_collection: - dependency: transitive + dependency: "direct main" description: name: built_collection sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100" @@ -146,7 +146,7 @@ packages: source: hosted version: "4.5.0" collection: - dependency: transitive + dependency: "direct main" description: name: collection sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 @@ -185,6 +185,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.3.2" + deep_collection: + dependency: "direct main" + description: + name: deep_collection + sha256: "59abffbfd9add6d47e04d623c50c3e2bcf3ed6a1606513dbe3325a738f77cb0c" + url: "https://pub.dev" + source: hosted + version: "1.0.2" dio: dependency: "direct main" description: @@ -201,6 +209,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.1.0" + effective_dart: + dependency: transitive + description: + name: effective_dart + sha256: "6a69783c808344084b65667e87ff600823531e95810a8a15882cb542fe22de80" + url: "https://pub.dev" + source: hosted + version: "1.3.2" equatable: dependency: "direct main" description: @@ -508,10 +524,10 @@ packages: dependency: "direct main" description: name: ory_client - sha256: a7cdf22fb8f057e773cd6764831aaf4dab7dc8b6cfefc643b5b545b574b214c9 + sha256: "151372511353601d4afd81213523ea0662be0f754b44b4be17c40c91bf1967cc" url: "https://pub.dev" source: hosted - version: "1.1.49" + version: "1.2.10" package_config: dependency: transitive description: diff --git a/flutter-ory-network/pubspec.yaml b/flutter-ory-network/pubspec.yaml index 2484a84..dcc0062 100644 --- a/flutter-ory-network/pubspec.yaml +++ b/flutter-ory-network/pubspec.yaml @@ -47,6 +47,9 @@ dependencies: dotenv: ^4.1.0 google_fonts: ^5.1.0 built_value: ^8.6.2 + deep_collection: ^1.0.2 + collection: ^1.17.2 + built_collection: ^5.1.1 dev_dependencies: flutter_test: