feat(authentication)!: rename package

This commit is contained in:
2022-04-19 19:34:04 +02:00
parent 22e0aeeeed
commit c70446e2c4
148 changed files with 7788 additions and 0 deletions
@@ -0,0 +1,18 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
export 'builder/authentication_builder.dart';
export 'cubit/authentication_cubit.dart';
@@ -0,0 +1,56 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:flutter/material.dart';
import 'package:wyatt_authentication_bloc/src/authentication/cubit/authentication_cubit.dart';
import 'package:wyatt_authentication_bloc/src/models/user/user_interface.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class AuthenticationBuilder extends StatelessWidget {
const AuthenticationBuilder({
Key? key,
required this.authenticated,
required this.unauthenticated,
required this.unknown,
}) : super(key: key);
final Widget Function(
BuildContext context,
UserInterface user,
Map<String, dynamic>? userData,
) authenticated;
final Widget Function(BuildContext context) unauthenticated;
final Widget Function(BuildContext context) unknown;
@override
Widget build(BuildContext context) {
return BlocBuilder<AuthenticationCubit, AuthenticationState>(
builder: (context, state) {
if (state.status == AuthenticationStatus.authenticated) {
if (state.user != null) {
return authenticated(context, state.user!, state.userData);
} else {
return unauthenticated(context);
}
} else if (state.status == AuthenticationStatus.unauthenticated) {
return unauthenticated(context);
} else {
return unknown(context);
}
},
);
}
}
@@ -0,0 +1,76 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:wyatt_authentication_bloc/src/models/user/user_interface.dart';
import 'package:wyatt_authentication_bloc/src/repositories/authentication_repository_interface.dart';
part 'authentication_state.dart';
class AuthenticationCubit extends Cubit<AuthenticationState> {
final AuthenticationRepositoryInterface _authenticationRepository;
StreamSubscription<UserInterface>? _userSubscription;
final Future<Map<String, dynamic>> Function(UserInterface user)?
_onAuthSuccess;
AuthenticationCubit({
required AuthenticationRepositoryInterface authenticationRepository,
Future<Map<String, dynamic>> Function(UserInterface user)? onAuthSuccess,
}) : _authenticationRepository = authenticationRepository,
_onAuthSuccess = onAuthSuccess,
super(const AuthenticationState.unknown());
Future<void> init() async {
final _firstUser = await _authenticationRepository.user.first;
start();
return changeStatus(_firstUser);
}
bool start() {
_userSubscription = _authenticationRepository.user.listen(changeStatus);
return true;
}
bool stop() {
_userSubscription?.cancel();
return true;
}
Future<void> changeStatus(UserInterface user) async {
if (user.isNotEmpty) {
final Map<String, dynamic>? userData = await _onAuthSuccess?.call(user);
emit(AuthenticationState.authenticated(user, userData));
} else {
stop();
emit(const AuthenticationState.unauthenticated());
}
}
void logOut() {
unawaited(_authenticationRepository.signOut());
}
@override
Future<void> close() {
_userSubscription?.cancel();
return super.close();
}
}
@@ -0,0 +1,58 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
part of 'authentication_cubit.dart';
enum AuthenticationStatus {
unknown,
authenticated,
unauthenticated,
}
class AuthenticationState extends Equatable {
final AuthenticationStatus status;
final UserInterface? user;
final Map<String, dynamic>? userData;
const AuthenticationState._({
required this.status,
this.user,
this.userData,
});
const AuthenticationState.unknown()
: this._(status: AuthenticationStatus.unknown);
const AuthenticationState.authenticated(
UserInterface user,
Map<String, dynamic>? userData,
) : this._(
status: AuthenticationStatus.authenticated,
user: user,
userData: userData,
);
const AuthenticationState.unauthenticated()
: this._(status: AuthenticationStatus.unauthenticated);
@override
List<Object?> get props => [status, user, userData];
@override
// ignore: lines_longer_than_80_chars
String toString() =>
'AuthenticationState(status: $status, user: $user, userData: $userData)';
}
@@ -0,0 +1,69 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:wyatt_authentication_bloc/src/form/form_status.dart';
import 'package:wyatt_authentication_bloc/src/models/exceptions/exceptions_interface.dart';
import 'package:wyatt_authentication_bloc/src/repositories/authentication_repository_interface.dart';
part 'email_verification_state.dart';
class EmailVerificationCubit extends Cubit<EmailVerificationState> {
final AuthenticationRepositoryInterface _authenticationRepository;
EmailVerificationCubit(this._authenticationRepository)
: super(const EmailVerificationState());
Future<void> sendEmailVerification() async {
emit(state.copyWith(status: FormStatus.submissionInProgress));
try {
await _authenticationRepository.sendEmailVerification();
emit(state.copyWith(status: FormStatus.submissionSuccess));
} on SendEmailVerificationFailureInterface catch (e) {
emit(
state.copyWith(
errorMessage: e.message,
status: FormStatus.submissionFailure,
),
);
} catch (_) {
emit(state.copyWith(status: FormStatus.submissionFailure));
}
}
Future<void> checkEmailVerification() async {
emit(state.copyWith(status: FormStatus.submissionInProgress));
try {
await _authenticationRepository.refresh();
emit(
state.copyWith(
isVerified: _authenticationRepository.currentUser.emailVerified,
status: FormStatus.submissionSuccess,
),
);
} on RefreshFailureInterface catch (e) {
emit(
state.copyWith(
errorMessage: e.message,
status: FormStatus.submissionFailure,
),
);
} catch (_) {
emit(state.copyWith(status: FormStatus.submissionFailure));
}
}
}
@@ -0,0 +1,44 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
part of 'email_verification_cubit.dart';
class EmailVerificationState extends Equatable {
final bool isVerified;
final FormStatus status;
final String? errorMessage;
const EmailVerificationState({
this.isVerified = false,
this.status = FormStatus.pure,
this.errorMessage,
});
EmailVerificationState copyWith({
bool? isVerified,
FormStatus? status,
String? errorMessage,
}) {
return EmailVerificationState(
isVerified: isVerified ?? this.isVerified,
status: status ?? this.status,
errorMessage: errorMessage ?? this.errorMessage,
);
}
@override
List<Object> get props => [isVerified, status];
}
@@ -0,0 +1,17 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
export 'cubit/email_verification_cubit.dart';
@@ -0,0 +1,22 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
export 'form_data.dart';
export 'form_entry.dart';
export 'form_input.dart';
export 'form_input_status.dart';
export 'form_status.dart';
export 'form_validator.dart';
@@ -0,0 +1,137 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:flutter/foundation.dart';
import 'package:wyatt_authentication_bloc/src/form/form_entry.dart';
import 'package:wyatt_authentication_bloc/src/form/form_input.dart';
import 'package:wyatt_authentication_bloc/src/models/validation_error.dart';
@immutable
class FormData {
const FormData(this._entries);
FormData.empty() : this(<FormEntry>[]);
final List<FormEntry> _entries;
List<FormEntry> get entries => _entries;
List<FormInput<T, ValidationError>> inputs<T>() {
return _entries
.map((FormEntry entry) => entry.input as FormInput<T, ValidationError>)
.toList();
}
FormInput<T, ValidationError> input<T>(String field) {
if (contains(field)) {
return _entries
.firstWhere((FormEntry entry) => entry.field == field)
.input as FormInput<T, ValidationError>;
} else {
throw Exception('Field $field does not exist in form');
}
}
bool contains(String field) {
return _entries.any((FormEntry entry) => entry.field == field);
}
FormData intersection(FormData other) {
final List<FormEntry> entries = <FormEntry>[];
for (final FormEntry entry in _entries) {
if (other.contains(entry.field)) {
entries.add(entry);
}
}
return FormData(entries);
}
FormData difference(FormData other) {
final List<FormEntry> entries = <FormEntry>[];
for (final FormEntry otherEntry in other._entries) {
if (!contains(otherEntry.field)) {
entries.add(otherEntry);
}
}
for (final FormEntry entry in _entries) {
if (!other.contains(entry.field)) {
entries.add(entry);
}
}
return FormData(entries);
}
FormData union(FormData other) {
final List<FormEntry> entries = <FormEntry>[];
for (final FormEntry entry in _entries) {
entries.add(entry);
}
for (final FormEntry otherEntry in other._entries) {
if (!contains(otherEntry.field)) {
entries.add(otherEntry);
}
}
return FormData(entries);
}
void update(String field, FormInput input) {
if (contains(field)) {
final index = _entries.indexOf(
_entries.firstWhere((FormEntry entry) => entry.field == field),
);
_entries[index] = _entries[index].copyWith(input: input);
}
}
FormData clone() {
return FormData(
_entries
.map((FormEntry entry) => entry.clone())
.toList(),
);
}
Map<String, dynamic> toMap() {
final map = <String, dynamic>{};
for (final entry in _entries) {
if (entry.export) {
map[entry.fieldName ?? entry.field] = entry.input.value;
}
}
return map;
}
@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
return other is FormData && listEquals(other._entries, _entries);
}
@override
int get hashCode => _entries.hashCode;
@override
String toString() => 'FormData(entries: $_entries)';
}
@@ -0,0 +1,77 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:flutter/foundation.dart';
import 'package:wyatt_authentication_bloc/src/form/form_input.dart';
@immutable
class FormEntry {
const FormEntry(this.field, this.input, {this.export = true, this.fieldName});
final String field;
final FormInput input;
final bool export;
final String? fieldName;
FormEntry copyWith({
String? field,
FormInput? input,
bool? export,
String? fieldName,
}) {
return FormEntry(
field ?? this.field,
input ?? this.input,
export: export ?? this.export,
fieldName: fieldName ?? this.fieldName,
);
}
FormEntry clone() {
return FormEntry(
field,
input,
export: export,
fieldName: fieldName,
);
}
@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
return other is FormEntry &&
other.field == field &&
other.input == input &&
other.export == export &&
other.fieldName == fieldName;
}
@override
int get hashCode {
return field.hashCode ^
input.hashCode ^
export.hashCode ^
fieldName.hashCode;
}
@override
String toString() {
// ignore: lines_longer_than_80_chars
return 'FormEntry(field: $field, input: $input, export: $export, fieldName: $fieldName)';
}
}
@@ -0,0 +1,111 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:flutter/foundation.dart';
import 'package:wyatt_authentication_bloc/src/form/form_input_status.dart';
/// {@template form_input}
/// A [FormInput] represents the value of a single form input field.
/// It contains information about the [FormInputStatus], [value], as well
/// as validation status.
///
/// [FormInput] should be extended to define custom [FormInput] instances.
///
/// ```dart
/// enum FirstNameError { empty }
/// class FirstName extends FormInput<String, FirstNameError> {
/// const FirstName.pure({String value = ''}) : super.pure(value);
/// const FirstName.dirty({String value = ''}) : super.dirty(value);
///
/// @override
/// FirstNameError? validator(String value) {
/// return value.isEmpty ? FirstNameError.empty : null;
/// }
/// }
/// ```
/// {@endtemplate}
@immutable
abstract class FormInput<T, E> {
const FormInput._(this.value, [this.pure = true]);
/// Constructor which create a `pure` [FormInput] with a given value.
const FormInput.pure(T value) : this._(value);
/// Constructor which create a `dirty` [FormInput] with a given value.
const FormInput.dirty(T value) : this._(value, false);
/// The value of the given [FormInput].
/// For example, if you have a `FormInput` for `FirstName`,
/// the value could be 'Joe'.
final T value;
/// If the [FormInput] is pure (has been touched/modified).
/// Typically when the `FormInput` is initially created,
/// it is created using the `FormInput.pure` constructor to
/// signify that the user has not modified it.
///
/// For subsequent changes (in response to user input), the
/// `FormInput.dirty` constructor should be used to signify that
/// the `FormInput` has been manipulated.
final bool pure;
/// The [FormInputStatus] which can be one of the following:
/// * [FormInputStatus.pure]
/// - if the input has not been modified.
/// * [FormInputStatus.invalid]
/// - if the input has been modified and validation failed.
/// * [FormInputStatus.valid]
/// - if the input has been modified and validation succeeded.
FormInputStatus get status => pure
? FormInputStatus.pure
: valid
? FormInputStatus.valid
: FormInputStatus.invalid;
/// Returns a validation error if the [FormInput] is invalid.
/// Returns `null` if the [FormInput] is valid.
E? get error => validator(value);
/// Whether the [FormInput] value is valid according to the
/// overridden `validator`.
///
/// Returns `true` if `validator` returns `null` for the
/// current [FormInput] value and `false` otherwise.
bool get valid => validator(value) == null;
/// Whether the [FormInput] value is not valid.
/// A value is invalid when the overridden `validator`
/// returns an error (non-null value).
bool get invalid => status == FormInputStatus.invalid;
/// A function that must return a validation error if the provided
/// [value] is invalid and `null` otherwise.
E? validator(T value);
@override
int get hashCode => value.hashCode ^ pure.hashCode;
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType) return false;
return other is FormInput<T, E> &&
other.value == value &&
other.pure == pure;
}
@override
String toString() => '$runtimeType($value, $pure)';
}
@@ -0,0 +1,27 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
/// Enum representing the status of a form input at any given point in time.
enum FormInputStatus {
/// The form input has not been touched.
pure,
/// The form input is valid.
valid,
/// The form input is not valid.
invalid,
}
@@ -0,0 +1,79 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
/// Enum representing the status of a form at any given point in time.
enum FormStatus {
/// The form has not been touched.
pure,
/// The form has been completely validated.
valid,
/// The form contains one or more invalid inputs.
invalid,
/// The form is in the process of being submitted.
submissionInProgress,
/// The form has been submitted successfully.
submissionSuccess,
/// The form submission failed.
submissionFailure,
/// The form submission has been canceled.
submissionCanceled
}
const Set<FormStatus> _validatedFormStatuses = <FormStatus>{
FormStatus.valid,
FormStatus.submissionInProgress,
FormStatus.submissionSuccess,
FormStatus.submissionFailure,
FormStatus.submissionCanceled,
};
/// Useful extensions on [FormStatus]
extension FormStatusX on FormStatus {
/// Indicates whether the form is untouched.
bool get isPure => this == FormStatus.pure;
/// Indicates whether the form is completely validated.
bool get isValid => this == FormStatus.valid;
/// Indicates whether the form has been validated successfully.
/// This means the [FormStatus] is either:
/// * `FormStatus.valid`
/// * `FormStatus.submissionInProgress`
/// * `FormStatus.submissionSuccess`
/// * `FormStatus.submissionFailure`
bool get isValidated => _validatedFormStatuses.contains(this);
/// Indicates whether the form contains one or more invalid inputs.
bool get isInvalid => this == FormStatus.invalid;
/// Indicates whether the form is in the process of being submitted.
bool get isSubmissionInProgress => this == FormStatus.submissionInProgress;
/// Indicates whether the form has been submitted successfully.
bool get isSubmissionSuccess => this == FormStatus.submissionSuccess;
/// Indicates whether the form submission failed.
bool get isSubmissionFailure => this == FormStatus.submissionFailure;
/// Indicates whether the form submission has been canceled.
bool get isSubmissionCanceled => this == FormStatus.submissionCanceled;
}
@@ -0,0 +1,32 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:wyatt_authentication_bloc/src/form/form_input.dart';
import 'package:wyatt_authentication_bloc/src/form/form_input_status.dart';
import 'package:wyatt_authentication_bloc/src/form/form_status.dart';
/// Class which contains methods that help manipulate and manage
/// [FormStatus] and [FormInputStatus] instances.
class FormValidator {
/// Returns a [FormStatus] given a list of [FormInput].
static FormStatus validate(List<FormInput> inputs) {
return inputs.every((FormInput element) => element.pure)
? FormStatus.pure
: inputs.any((FormInput input) => input.valid == false)
? FormStatus.invalid
: FormStatus.valid;
}
}
@@ -0,0 +1,35 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:wyatt_authentication_bloc/src/form/form_input.dart';
import 'package:wyatt_authentication_bloc/src/models/validation_error.dart';
/// {@template boolean}
/// Form input for a bool input
/// {@endtemplate}
class Boolean extends FormInput<bool, ValidationError> {
/// {@macro boolean}
const Boolean.pure({bool? defaultValue = false})
: super.pure(defaultValue ?? false);
/// {@macro boolean}
const Boolean.dirty({bool value = false}) : super.dirty(value);
@override
ValidationError? validator(bool? value) {
return value != null ? null : ValidationError.invalid;
}
}
@@ -0,0 +1,39 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:wyatt_authentication_bloc/src/form/form_input.dart';
import 'package:wyatt_authentication_bloc/src/models/validation_error.dart';
/// {@template confirmed_password}
/// Form input for a confirmed password input.
/// {@endtemplate}
class ConfirmedPassword
extends FormInput<String, ValidationError> {
/// {@macro confirmed_password}
const ConfirmedPassword.pure({this.password = ''}) : super.pure('');
/// {@macro confirmed_password}
const ConfirmedPassword.dirty({required this.password, String value = ''})
: super.dirty(value);
/// The original password.
final String password;
@override
ValidationError? validator(String? value) {
return password == value ? null : ValidationError.invalid;
}
}
@@ -0,0 +1,40 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:wyatt_authentication_bloc/src/form/form_input.dart';
import 'package:wyatt_authentication_bloc/src/models/validation_error.dart';
/// {@template email}
/// Form input for an email input.
/// {@endtemplate}
class Email extends FormInput<String, ValidationError> {
/// {@macro email}
const Email.pure() : super.pure('');
/// {@macro email}
const Email.dirty([String value = '']) : super.dirty(value);
static final RegExp _emailRegExp = RegExp(
r'^[a-zA-Z0-9.!#$%&*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$',
);
@override
ValidationError? validator(String? value) {
return _emailRegExp.hasMatch(value ?? '')
? null
: ValidationError.invalid;
}
}
@@ -0,0 +1,18 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
export 'exceptions_firebase.dart';
export 'exceptions_interface.dart';
@@ -0,0 +1,252 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:wyatt_authentication_bloc/src/models/exceptions/exceptions_interface.dart';
class ApplyActionCodeFailureFirebase extends ApplyActionCodeFailureInterface {
ApplyActionCodeFailureFirebase([String? code, String? message])
: super(code ?? 'unknown', message ?? 'An unknown error occurred.');
ApplyActionCodeFailureFirebase.fromCode(String code) : super.fromCode(code) {
switch (code) {
case 'expired-action-code':
message = 'Action code has expired.';
break;
case 'invalid-action-code':
message = 'Action code is invalid.';
break;
case 'user-disabled':
message =
'This user has been disabled. Please contact support for help.';
break;
case 'user-not-found':
message = 'Email is not found, please create an account.';
break;
default:
this.code = 'unknown';
message = 'An unknown error occurred.';
}
}
}
class SignUpWithEmailAndPasswordFailureFirebase
extends SignUpWithEmailAndPasswordFailureInterface {
SignUpWithEmailAndPasswordFailureFirebase([String? code, String? message])
: super(code ?? 'unknown', message ?? 'An unknown error occurred.');
SignUpWithEmailAndPasswordFailureFirebase.fromCode(String code)
: super.fromCode(code) {
switch (code) {
case 'invalid-email':
message = 'The email address is badly formatted.';
break;
case 'user-disabled':
message =
'This user has been disabled. Please contact support for help.';
break;
case 'email-already-in-use':
message = 'An account already exists for that email.';
break;
case 'operation-not-allowed':
message = 'Operation is not allowed. Please contact support.';
break;
case 'weak-password':
message = 'Please enter a stronger password.';
break;
default:
this.code = 'unknown';
message = 'An unknown error occurred.';
}
}
}
class FetchSignInMethodsForEmailFailureFirebase
extends FetchSignInMethodsForEmailFailureInterface {
FetchSignInMethodsForEmailFailureFirebase([String? code, String? message])
: super(code ?? 'unknown', message ?? 'An unknown error occurred.');
FetchSignInMethodsForEmailFailureFirebase.fromCode(String code)
: super.fromCode(code) {
switch (code) {
case 'invalid-email':
message = 'The email address is badly formatted.';
break;
default:
this.code = 'unknown';
message = 'An unknown error occurred.';
}
}
}
class SignInAnonymouslyFailureFirebase
extends SignInAnonymouslyFailureInterface {
SignInAnonymouslyFailureFirebase([String? code, String? message])
: super(code ?? 'unknown', message ?? 'An unknown error occurred.');
SignInAnonymouslyFailureFirebase.fromCode(String code)
: super.fromCode(code) {
switch (code) {
case 'operation-not-allowed':
message = 'Operation is not allowed. Please contact support.';
break;
default:
this.code = 'unknown';
message = 'An unknown error occurred.';
}
}
}
class SignInWithGoogleFailureFirebase extends SignInWithGoogleFailureInterface {
SignInWithGoogleFailureFirebase([String? code, String? message])
: super(code ?? 'unknown', message ?? 'An unknown error occurred.');
SignInWithGoogleFailureFirebase.fromCode(String code) : super.fromCode(code) {
switch (code) {
case 'account-exists-with-different-credential':
message = 'Account exists with different credentials.';
break;
case 'invalid-credential':
message = 'The credential received is malformed or has expired.';
break;
case 'operation-not-allowed':
message = 'Operation is not allowed. Please contact support.';
break;
case 'user-disabled':
message =
'This user has been disabled. Please contact support for help.';
break;
case 'user-not-found':
message = 'Email is not found, please create an account.';
break;
case 'wrong-password':
message = 'Incorrect password, please try again.';
break;
case 'invalid-verification-code':
message = 'The credential verification code received is invalid.';
break;
case 'invalid-verification-id':
message = 'The credential verification ID received is invalid.';
break;
default:
this.code = 'unknown';
message = 'An unknown error occurred.';
}
}
}
class SignInWithEmailLinkFailureFirebase
extends SignInWithEmailLinkFailureInterface {
SignInWithEmailLinkFailureFirebase([String? code, String? message])
: super(code ?? 'unknown', message ?? 'An unknown error occurred.');
SignInWithEmailLinkFailureFirebase.fromCode(String code)
: super.fromCode(code) {
switch (code) {
case 'expired-action-code':
message = 'Action code has expired.';
break;
case 'invalid-email':
message = 'Email is not valid or badly formatted.';
break;
case 'user-disabled':
message =
'This user has been disabled. Please contact support for help.';
break;
default:
this.code = 'unknown';
message = 'An unknown error occurred.';
}
}
}
class SignInWithEmailAndPasswordFailureFirebase
extends SignInWithEmailAndPasswordFailureInterface {
SignInWithEmailAndPasswordFailureFirebase([String? code, String? message])
: super(code ?? 'unknown', message ?? 'An unknown error occurred.');
SignInWithEmailAndPasswordFailureFirebase.fromCode(String code)
: super.fromCode(code) {
switch (code) {
case 'invalid-email':
message = 'Email is not valid or badly formatted.';
break;
case 'user-disabled':
message =
'This user has been disabled. Please contact support for help.';
break;
case 'user-not-found':
message = 'Email is not found, please create an account.';
break;
case 'wrong-password':
message = 'Incorrect password, please try again.';
break;
default:
this.code = 'unknown';
message = 'An unknown error occurred.';
}
}
}
class SendEmailVerificationFailureFirebase
extends SendEmailVerificationFailureInterface {
SendEmailVerificationFailureFirebase([String? code, String? message])
: super(code ?? 'unknown', message ?? 'An unknown error occurred.');
SendEmailVerificationFailureFirebase.fromCode(String code)
: super.fromCode(code);
}
class SendPasswordResetEmailFailureFirebase
extends SendPasswordResetEmailFailureInterface {
SendPasswordResetEmailFailureFirebase([String? code, String? message])
: super(code ?? 'unknown', message ?? 'An unknown error occurred.');
SendPasswordResetEmailFailureFirebase.fromCode(String code)
: super.fromCode(code);
}
class SendSignInLinkEmailFailureFirebase
extends SendSignInLinkEmailFailureInterface {
SendSignInLinkEmailFailureFirebase([String? code, String? message])
: super(code ?? 'unknown', message ?? 'An unknown error occurred.');
SendSignInLinkEmailFailureFirebase.fromCode(String code)
: super.fromCode(code);
}
class ConfirmPasswordResetFailureFirebase
extends ConfirmPasswordResetFailureInterface {
ConfirmPasswordResetFailureFirebase([String? code, String? message])
: super(code ?? 'unknown', message ?? 'An unknown error occurred.');
ConfirmPasswordResetFailureFirebase.fromCode(String code)
: super.fromCode(code);
}
class VerifyPasswordResetCodeFailureFirebase
extends VerifyPasswordResetCodeFailureInterface {
VerifyPasswordResetCodeFailureFirebase([String? code, String? message])
: super(code ?? 'unknown', message ?? 'An unknown error occurred.');
VerifyPasswordResetCodeFailureFirebase.fromCode(String code)
: super.fromCode(code);
}
class RefreshFailureFirebase extends RefreshFailureInterface {
RefreshFailureFirebase([String? code, String? message])
: super(code ?? 'unknown', message ?? 'An unknown error occurred.');
RefreshFailureFirebase.fromCode(String code) : super.fromCode(code);
}
class SignOutFailureFirebase extends SignOutFailureInterface {
SignOutFailureFirebase([String? code, String? message])
: super(code ?? 'unknown', message ?? 'An unknown error occurred.');
SignOutFailureFirebase.fromCode(String code) : super.fromCode(code);
}
@@ -0,0 +1,212 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
abstract class AuthenticationFailureInterface implements Exception {
String code;
String message;
AuthenticationFailureInterface(this.code, this.message);
AuthenticationFailureInterface.fromCode(this.code)
: message = 'An unknown error occurred.';
}
/// {@template apply_action_code_failure}
/// Thrown if during the apply action code process if a failure occurs.
/// {@endtemplate}
abstract class ApplyActionCodeFailureInterface
extends AuthenticationFailureInterface {
/// {@macro apply_action_code_failure}
ApplyActionCodeFailureInterface(String code, String message)
: super(code, message);
/// {@macro apply_action_code_failure}
ApplyActionCodeFailureInterface.fromCode(String code) : super.fromCode(code);
}
/// {@template sign_up_with_email_and_password_failure}
/// Thrown if during the sign up process if a failure occurs.
/// {@endtemplate}
abstract class SignUpWithEmailAndPasswordFailureInterface
extends AuthenticationFailureInterface {
/// {@macro sign_up_with_email_and_password_failure}
SignUpWithEmailAndPasswordFailureInterface(String code, String message)
: super(code, message);
/// {@macro sign_up_with_email_and_password_failure}
SignUpWithEmailAndPasswordFailureInterface.fromCode(String code)
: super.fromCode(code);
}
/// {@template fetch_sign_in_methods_failure}
/// Thrown during the fetch sign in methods if a failure occurs.
/// {@endtemplate}
abstract class FetchSignInMethodsForEmailFailureInterface
extends AuthenticationFailureInterface {
/// {@macro fetch_sign_in_methods_failure}
FetchSignInMethodsForEmailFailureInterface(String code, String message)
: super(code, message);
/// {@macro fetch_sign_in_methods_failure}
FetchSignInMethodsForEmailFailureInterface.fromCode(String code)
: super.fromCode(code);
}
/// {@template sign_in_anonymously_failure}
/// Thrown during the sign in process if a failure occurs.
/// {@endtemplate}
abstract class SignInAnonymouslyFailureInterface
extends AuthenticationFailureInterface {
/// {@macro sign_in_anonymously_failure}
SignInAnonymouslyFailureInterface(String code, String message)
: super(code, message);
/// {@macro sign_in_anonymously_failure}
SignInAnonymouslyFailureInterface.fromCode(String code)
: super.fromCode(code);
}
/// {@template sign_in_with_google_failure}
/// Thrown during the sign in process if a failure occurs.
/// {@endtemplate}
abstract class SignInWithGoogleFailureInterface
extends AuthenticationFailureInterface {
/// {@macro sign_in_with_google_failure}
SignInWithGoogleFailureInterface(String code, String message)
: super(code, message);
/// {@macro sign_in_with_google_failure}
SignInWithGoogleFailureInterface.fromCode(String code) : super.fromCode(code);
}
/// {@template sign_in_with_email_link_failure}
/// Thrown during the sign in process if a failure occurs.
/// {@endtemplate}
abstract class SignInWithEmailLinkFailureInterface
extends AuthenticationFailureInterface {
/// {@macro sign_in_with_email_link_failure}
SignInWithEmailLinkFailureInterface(String code, String message)
: super(code, message);
/// {@macro sign_in_with_email_link_failure}
SignInWithEmailLinkFailureInterface.fromCode(String code)
: super.fromCode(code);
}
/// {@template sign_in_with_email_and_password_failure}
/// Thrown during the sign in process if a failure occurs.
/// {@endtemplate}
abstract class SignInWithEmailAndPasswordFailureInterface
extends AuthenticationFailureInterface {
/// {@macro sign_in_with_email_and_password_failure}
SignInWithEmailAndPasswordFailureInterface(String code, String message)
: super(code, message);
/// {@macro sign_in_with_email_and_password_failure}
SignInWithEmailAndPasswordFailureInterface.fromCode(String code)
: super.fromCode(code);
}
/// {@template send_email_verification_failure}
/// Thrown during the email verification process if a failure occurs.
/// {@endtemplate}
abstract class SendEmailVerificationFailureInterface
extends AuthenticationFailureInterface {
/// {@macro send_email_verification_failure}
SendEmailVerificationFailureInterface(String code, String message)
: super(code, message);
/// {@macro send_email_verification_failure}
SendEmailVerificationFailureInterface.fromCode(String code)
: super.fromCode(code);
}
/// {@template send_password_reset_email_failure}
/// Thrown during the password reset process if a failure occurs.
/// {@endtemplate}
abstract class SendPasswordResetEmailFailureInterface
extends AuthenticationFailureInterface {
/// {@macro send_password_reset_email_failure}
SendPasswordResetEmailFailureInterface(String code, String message)
: super(code, message);
/// {@macro send_password_reset_email_failure}
SendPasswordResetEmailFailureInterface.fromCode(String code)
: super.fromCode(code);
}
/// {@template send_sign_in_link_email_failure}
/// Thrown during the sign in link process if a failure occurs.
/// {@endtemplate}
abstract class SendSignInLinkEmailFailureInterface
extends AuthenticationFailureInterface {
/// {@macro send_sign_in_link_email_failure}
SendSignInLinkEmailFailureInterface(String code, String message)
: super(code, message);
/// {@macro send_sign_in_link_email_failure}
SendSignInLinkEmailFailureInterface.fromCode(String code)
: super.fromCode(code);
}
/// {@template confirm_password_reset_failure}
/// Thrown during the password reset process if a failure occurs.
/// {@endtemplate}
abstract class ConfirmPasswordResetFailureInterface
extends AuthenticationFailureInterface {
/// {@macro confirm_password_reset_failure}
ConfirmPasswordResetFailureInterface(String code, String message)
: super(code, message);
/// {@macro confirm_password_reset_failure}
ConfirmPasswordResetFailureInterface.fromCode(String code)
: super.fromCode(code);
}
/// {@template verify_password_reset_code_failure}
/// Thrown during the password reset process if a failure occurs.
/// {@endtemplate}
abstract class VerifyPasswordResetCodeFailureInterface
extends AuthenticationFailureInterface {
/// {@macro verify_password_reset_code_failure}
VerifyPasswordResetCodeFailureInterface(String code, String message)
: super(code, message);
/// {@macro verify_password_reset_code_failure}
VerifyPasswordResetCodeFailureInterface.fromCode(String code)
: super.fromCode(code);
}
/// {@template refresh_failure}
/// Thrown during the refresh process if a failure occurs.
/// {@endtemplate}
abstract class RefreshFailureInterface extends AuthenticationFailureInterface {
/// {@macro refresh_failure}
RefreshFailureInterface(String code, String message) : super(code, message);
/// {@macro refresh_failure}
RefreshFailureInterface.fromCode(String code) : super.fromCode(code);
}
/// {@template sign_out_failure}
/// Thrown during the sign out process if a failure occurs.
/// {@endtemplate}
abstract class SignOutFailureInterface extends AuthenticationFailureInterface {
/// {@macro sign_out_failure}
SignOutFailureInterface(String code, String message) : super(code, message);
/// {@macro sign_out_failure}
SignOutFailureInterface.fromCode(String code) : super.fromCode(code);
}
@@ -0,0 +1,38 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:wyatt_authentication_bloc/src/form/form_input.dart';
import 'package:wyatt_authentication_bloc/src/models/validation_error.dart';
/// {@template iban}
/// Form input for an IBAN input.
/// {@endtemplate}
class Iban extends FormInput<String, ValidationError> {
/// {@macro iban}
const Iban.pure() : super.pure('');
/// {@macro iban}
const Iban.dirty([String value = '']) : super.dirty(value);
static final RegExp _regExp = RegExp(
r'^(?:((?:IT|SM)\d{2}[A-Z]{1}\d{22})|(NL\d{2}[A-Z]{4}\d{10})|(LV\d{2}[A-Z]{4}\d{13})|((?:BG|GB|IE)\d{2}[A-Z]{4}\d{14})|(GI\d{2}[A-Z]{4}\d{15})|(RO\d{2}[A-Z]{4}\d{16})|(MT\d{2}[A-Z]{4}\d{23})|(NO\d{13})|((?:DK|FI)\d{16})|((?:SI)\d{17})|((?:AT|EE|LU|LT)\d{18})|((?:HR|LI|CH)\d{19})|((?:DE|VA)\d{20})|((?:AD|CZ|ES|MD|SK|SE)\d{22})|(PT\d{23})|((?:IS)\d{24})|((?:BE)\d{14})|((?:FR|MC|GR)\d{25})|((?:PL|HU|CY)\d{26}))$',
);
@override
ValidationError? validator(String? value) {
return _regExp.hasMatch(value ?? '') ? null : ValidationError.invalid;
}
}
@@ -0,0 +1,29 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
export 'boolean.dart';
export 'confirmed_password.dart';
export 'email.dart';
export 'exceptions/exceptions.dart';
export 'iban.dart';
export 'name.dart';
export 'operations.dart';
export 'password.dart';
export 'phone.dart';
export 'siren.dart';
export 'text_string.dart';
export 'user/user.dart';
export 'validation_error.dart';
@@ -0,0 +1,36 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:wyatt_authentication_bloc/src/form/form_input.dart';
import 'package:wyatt_authentication_bloc/src/models/validation_error.dart';
/// {@template name}
/// Form input for a name input.
/// {@endtemplate}
class Name extends FormInput<String, ValidationError> {
/// {@macro name}
const Name.pure() : super.pure('');
/// {@macro name}
const Name.dirty([String value = '']) : super.dirty(value);
static final RegExp _nameRegExp = RegExp(r"^([ \u00c0-\u01ffa-zA-Z'\-])+$");
@override
ValidationError? validator(String? value) {
return _nameRegExp.hasMatch(value ?? '') ? null : ValidationError.invalid;
}
}
@@ -0,0 +1,17 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
enum SetOperation { replace, intersection, difference, union }
@@ -0,0 +1,39 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:wyatt_authentication_bloc/src/form/form_input.dart';
import 'package:wyatt_authentication_bloc/src/models/validation_error.dart';
/// {@template password}
/// Form input for a password input.
/// {@endtemplate}
class Password extends FormInput<String, ValidationError> {
/// {@macro password}
const Password.pure() : super.pure('');
/// {@macro password}
const Password.dirty([String value = '']) : super.dirty(value);
static final RegExp _passwordRegExp =
RegExp(r'^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{8,}$');
@override
ValidationError? validator(String? value) {
return _passwordRegExp.hasMatch(value ?? '')
? null
: ValidationError.invalid;
}
}
@@ -0,0 +1,37 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:wyatt_authentication_bloc/src/form/form_input.dart';
import 'package:wyatt_authentication_bloc/src/models/validation_error.dart';
/// {@template phone}
/// Form input for a phone input.
/// {@endtemplate}
class Phone extends FormInput<String, ValidationError> {
/// {@macro phone}
const Phone.pure() : super.pure('');
/// {@macro phone}
const Phone.dirty([String value = '']) : super.dirty(value);
static final RegExp _phoneRegExp =
RegExp(r'^[\+]?[(]?[0-9]{3}[)]?[-\s\.]?[0-9]{3}[-\s\.]?[0-9]{4,6}$');
@override
ValidationError? validator(String? value) {
return _phoneRegExp.hasMatch(value ?? '') ? null : ValidationError.invalid;
}
}
@@ -0,0 +1,36 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:wyatt_authentication_bloc/src/form/form_input.dart';
import 'package:wyatt_authentication_bloc/src/models/validation_error.dart';
/// {@template siren}
/// Form input for a SIREN input.
/// {@endtemplate}
class Siren extends FormInput<String, ValidationError> {
/// {@macro siren}
const Siren.pure() : super.pure('');
/// {@macro siren}
const Siren.dirty([String value = '']) : super.dirty(value);
static final RegExp _regExp = RegExp(r'(\d{9}|\d{3}[ ]\d{3}[ ]\d{3})$');
@override
ValidationError? validator(String? value) {
return _regExp.hasMatch(value ?? '') ? null : ValidationError.invalid;
}
}
@@ -0,0 +1,36 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:wyatt_authentication_bloc/src/form/form_input.dart';
import 'package:wyatt_authentication_bloc/src/models/validation_error.dart';
/// {@template text_string}
/// Form input for a text input
/// {@endtemplate}
class TextString extends FormInput<String, ValidationError> {
/// {@macro text_string}
const TextString.pure() : super.pure('');
/// {@macro text_string}
const TextString.dirty([String value = '']) : super.dirty(value);
@override
ValidationError? validator(String? value) {
return (value?.isNotEmpty ?? false)
? null
: ValidationError.invalid;
}
}
@@ -0,0 +1,18 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
export 'user_firebase.dart';
export 'user_interface.dart';
@@ -0,0 +1,80 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:firebase_auth/firebase_auth.dart';
import 'package:wyatt_authentication_bloc/src/models/user/user_interface.dart';
class UserFirebase implements UserInterface {
final User? _user;
const UserFirebase(User user) : _user = user;
User? get inner => _user;
@override
const UserFirebase.empty() : _user = null;
@override
DateTime? get creationTime => _user?.metadata.creationTime;
@override
String? get displayName => _user?.displayName;
@override
String? get email => _user?.email;
@override
bool get emailVerified => _user?.emailVerified ?? false;
@override
bool get isAnonymous => _user?.isAnonymous ?? false;
@override
bool get isEmpty => _user == null;
@override
bool get isNotEmpty => _user != null;
@override
DateTime? get lastSignInTime => _user?.metadata.lastSignInTime;
@override
String? get phoneNumber => _user?.phoneNumber;
@override
String? get photoURL => _user?.photoURL;
@override
String? get refreshToken => _user?.refreshToken;
@override
String get uid => _user?.uid ?? '';
@override
bool? get isNewUser {
if (_user?.metadata.lastSignInTime == null ||
_user?.metadata.creationTime == null) {
return null;
} else {
return _user?.metadata.lastSignInTime == _user?.metadata.creationTime;
}
}
@override
// ignore: lines_longer_than_80_chars
String toString() => 'UserFirebase(creationTime: $creationTime, displayName: $displayName, email: $email, emailVerified: $emailVerified, isAnonymous: $isAnonymous, lastSignInTime: $lastSignInTime, phoneNumber: $phoneNumber, photoURL: $photoURL, refreshToken: $refreshToken, uid: $uid)';
}
@@ -0,0 +1,82 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
abstract class UserInterface {
/// The empty user constructor.
const UserInterface.empty();
/// The users display name.
///
/// Will be `null` if signing in anonymously or via password authentication.
String? get displayName;
/// The users email address.
///
/// Will be `null` if signing in anonymously.
String? get email;
/// Returns whether the users email address has been verified.
///
/// To send a verification email, see `SendEmailVerification`.
///
/// Once verified, call `reload` to ensure the latest user information is
/// retrieved from Firebase.
bool get emailVerified;
/// Returns whether the user is a anonymous.
bool get isAnonymous;
/// Returns the users account creation time.
///
/// When this account was created as dictated by the server clock.
DateTime? get creationTime;
/// When the user last signed in as dictated by the server clock.
///
/// This is only accurate up to a granularity of 2 minutes for consecutive
/// sign-in attempts.
DateTime? get lastSignInTime;
/// Returns the users phone number.
///
/// This property will be `null` if the user has not signed in or been has
/// their phone number linked.
String? get phoneNumber;
/// Returns a photo URL for the user.
///
/// This property will be populated if the user has signed in or been linked
/// with a 3rd party OAuth provider (such as Google).
String? get photoURL;
/// Returns a JWT refresh token for the user.
///
/// This property maybe `null` or empty if the underlying platform does not
/// support providing refresh tokens.
String? get refreshToken;
/// The user's unique ID.
String get uid;
/// Whether the user account has been recently created.
bool? get isNewUser;
/// Convenience getter to determine whether the current user is empty.
bool get isEmpty;
/// Convenience getter to determine whether the current user is not empty.
bool get isNotEmpty;
}
@@ -0,0 +1,20 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
enum ValidationError {
/// Generic invalid error.
invalid
}
@@ -0,0 +1,62 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:wyatt_authentication_bloc/src/form/form_status.dart';
import 'package:wyatt_authentication_bloc/src/form/form_validator.dart';
import 'package:wyatt_authentication_bloc/src/models/email.dart';
import 'package:wyatt_authentication_bloc/src/models/exceptions/exceptions_interface.dart';
import 'package:wyatt_authentication_bloc/src/repositories/authentication_repository_interface.dart';
part 'password_reset_state.dart';
class PasswordResetCubit extends Cubit<PasswordResetState> {
final AuthenticationRepositoryInterface _authenticationRepository;
PasswordResetCubit(this._authenticationRepository)
: super(const PasswordResetState());
void emailChanged(String value) {
final Email email = Email.dirty(value);
emit(
state.copyWith(
email: email,
status: FormValidator.validate([email]),
),
);
}
Future<void> sendPasswordResetEmail() async {
if (!state.status.isValidated) return;
emit(state.copyWith(status: FormStatus.submissionInProgress));
try {
await _authenticationRepository.sendPasswordResetEmail(
email: state.email.value,
);
emit(state.copyWith(status: FormStatus.submissionSuccess));
} on SendPasswordResetEmailFailureInterface catch (e) {
emit(
state.copyWith(
errorMessage: e.message,
status: FormStatus.submissionFailure,
),
);
} catch (_) {
emit(state.copyWith(status: FormStatus.submissionFailure));
}
}
}
@@ -0,0 +1,44 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
part of 'password_reset_cubit.dart';
class PasswordResetState extends Equatable {
final Email email;
final FormStatus status;
final String? errorMessage;
const PasswordResetState({
this.email = const Email.pure(),
this.status = FormStatus.pure,
this.errorMessage,
});
PasswordResetState copyWith({
Email? email,
FormStatus? status,
String? errorMessage,
}) {
return PasswordResetState(
email: email ?? this.email,
status: status ?? this.status,
errorMessage: errorMessage ?? this.errorMessage,
);
}
@override
List<Object> get props => [email, status];
}
@@ -0,0 +1,17 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
export 'cubit/password_reset_cubit.dart';
@@ -0,0 +1,226 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:firebase_auth/firebase_auth.dart';
import 'package:wyatt_authentication_bloc/src/models/exceptions/exceptions_firebase.dart';
import 'package:wyatt_authentication_bloc/src/models/user/user_firebase.dart';
import 'package:wyatt_authentication_bloc/src/models/user/user_interface.dart';
import 'package:wyatt_authentication_bloc/src/repositories/authentication_repository_interface.dart';
class AuthenticationRepositoryFirebase
implements AuthenticationRepositoryInterface {
final FirebaseAuth _firebaseAuth;
UserFirebase _userCache = const UserFirebase.empty();
AuthenticationRepositoryFirebase({FirebaseAuth? firebaseAuth})
: _firebaseAuth = firebaseAuth ?? FirebaseAuth.instance;
@override
Stream<UserInterface> get user {
return _firebaseAuth.userChanges().map((User? firebaseUser) {
final UserFirebase user = firebaseUser == null
? const UserFirebase.empty()
: firebaseUser.model;
_userCache = user;
return user;
});
}
@override
UserInterface get currentUser {
return _userCache;
}
@override
Future<void> applyActionCode(String code) async {
try {
await _firebaseAuth.applyActionCode(code);
} on FirebaseAuthException catch (e) {
throw ApplyActionCodeFailureFirebase.fromCode(e.code);
} catch (_) {
throw ApplyActionCodeFailureFirebase();
}
}
@override
Future<String?> signUp({
required String email,
required String password,
}) async {
try {
final creds = await _firebaseAuth.createUserWithEmailAndPassword(
email: email,
password: password,
);
return creds.user?.uid;
} on FirebaseAuthException catch (e) {
throw SignUpWithEmailAndPasswordFailureFirebase.fromCode(e.code);
} catch (_) {
throw SignUpWithEmailAndPasswordFailureFirebase();
}
}
@override
Future<List<String>> fetchSignInMethodsForEmail({
required String email,
}) async {
try {
return await _firebaseAuth.fetchSignInMethodsForEmail(email);
} on FirebaseAuthException catch (e) {
throw FetchSignInMethodsForEmailFailureFirebase.fromCode(e.code);
} catch (_) {
throw FetchSignInMethodsForEmailFailureFirebase();
}
}
@override
Future<void> signInAnonymously() async {
try {
await _firebaseAuth.signInAnonymously();
} on FirebaseAuthException catch (e) {
throw SignInAnonymouslyFailureFirebase.fromCode(e.code);
} catch (_) {
throw SignInAnonymouslyFailureFirebase();
}
}
@override
Future<void> signInWithGoogle() {
// TODO(hpcl): implement signInWithGoogle
throw UnimplementedError();
}
@override
Future<void> signInWithEmailLink(String email, String emailLink) async {
try {
await _firebaseAuth.signInWithEmailLink(
email: email,
emailLink: emailLink,
);
} on FirebaseAuthException catch (e) {
throw SignInWithEmailLinkFailureFirebase.fromCode(e.code);
} catch (_) {
throw SignInWithEmailLinkFailureFirebase();
}
}
@override
Future<void> signInWithEmailAndPassword({
required String email,
required String password,
}) async {
try {
await _firebaseAuth.signInWithEmailAndPassword(
email: email,
password: password,
);
} on FirebaseAuthException catch (e) {
throw SignInWithEmailAndPasswordFailureFirebase.fromCode(e.code);
} catch (_) {
throw SignInWithEmailAndPasswordFailureFirebase();
}
}
@override
Future<void> sendEmailVerification() async {
try {
await _userCache.inner!.sendEmailVerification();
} catch (e) {
throw SendEmailVerificationFailureFirebase();
}
}
@override
Future<void> sendPasswordResetEmail({required String email}) async {
try {
await _firebaseAuth.sendPasswordResetEmail(email: email);
} on FirebaseAuthException catch (e) {
throw SendPasswordResetEmailFailureFirebase.fromCode(e.code);
} catch (_) {
throw SendPasswordResetEmailFailureFirebase();
}
}
@override
Future<void> sendSignInLinkEmail({required String email}) async {
try {
// TODO(hpcl): implement sendSignInLinkEmail
} on FirebaseAuthException catch (e) {
throw SendSignInLinkEmailFailureFirebase.fromCode(e.code);
} catch (_) {
throw SendSignInLinkEmailFailureFirebase();
}
}
@override
Future<void> confirmPasswordReset({
required String code,
required String newPassword,
}) async {
try {
await _firebaseAuth.confirmPasswordReset(
code: code,
newPassword: newPassword,
);
} on FirebaseAuthException catch (e) {
throw ConfirmPasswordResetFailureFirebase.fromCode(e.code);
} catch (_) {
throw ConfirmPasswordResetFailureFirebase();
}
throw UnimplementedError();
}
@override
Future<void> verifyPasswordResetCode({required String code}) async {
try {
await _firebaseAuth.verifyPasswordResetCode(code);
} on FirebaseAuthException catch (e) {
throw VerifyPasswordResetCodeFailureFirebase.fromCode(e.code);
} catch (_) {
throw VerifyPasswordResetCodeFailureFirebase();
}
}
@override
Future<void> signOut() async {
try {
await Future.wait([
_firebaseAuth.signOut(),
]);
_userCache = const UserFirebase.empty();
} catch (_) {
throw SignOutFailureFirebase();
}
}
@override
Future<void> refresh() async {
try {
await _userCache.inner!.reload();
} on FirebaseAuthException catch (e) {
throw RefreshFailureFirebase.fromCode(e.code);
} catch (_) {
throw RefreshFailureFirebase();
}
}
}
extension on User {
UserFirebase get model {
return UserFirebase(this);
}
}
@@ -0,0 +1,117 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:wyatt_authentication_bloc/src/models/exceptions/exceptions_interface.dart';
import 'package:wyatt_authentication_bloc/src/models/user/user_interface.dart';
/// {@template authentication_repository}
/// Repository which manages user authentication.
/// {@endtemplate}
abstract class AuthenticationRepositoryInterface {
/// Stream of [UserInterface] which will emit the current user when
/// the authentication state changes.
///
/// Emits [UserInterface.empty] if the user is not authenticated.
Stream<UserInterface> get user;
/// Returns the current cached account.
/// Defaults to [UserInterface.empty] if there is no cached user.
UserInterface get currentUser;
/// Applies action code
///
/// [code] - The action code sent to the user's email address.
/// Throw [ApplyActionCodeFailureInterface] if an exception occurs.
Future<void> applyActionCode(String code);
/// Creates a new user with the provided [email] and [password].
///
/// Returns the newly created user's unique identifier.
///
/// Throws a [SignUpWithEmailAndPasswordFailureInterface] if
/// an exception occurs.
Future<String?> signUp({required String email, required String password});
/// Fetches sign in methods for [email].
///
/// Throws a [FetchSignInMethodsForEmailFailureInterface] if
/// an exception occurs.
Future<List<String>> fetchSignInMethodsForEmail({required String email});
/// Sign in anonimously.
///
/// Throws a [SignInAnonymouslyFailureInterface] if an exception occurs.
Future<void> signInAnonymously();
/// Starts the Sign In with Google Flow.
///
/// Throws a [SignInWithGoogleFailureInterface] if an exception occurs.
Future<void> signInWithGoogle();
/// Signs in using an email address and email sign-in link.
///
/// Throws a [SignInWithEmailLinkFailureInterface] if an exception occurs.
Future<void> signInWithEmailLink(
String email,
String emailLink,
);
/// Signs in with the provided [email] and [password].
///
/// Throws a [SignInWithEmailAndPasswordFailureInterface] if
/// an exception occurs.
Future<void> signInWithEmailAndPassword({
required String email,
required String password,
});
/// Sends verification email to the provided [user].
///
/// Throws a [SendEmailVerificationFailureInterface] if an exception occurs.
Future<void> sendEmailVerification();
/// Sends a password reset email to the provided [email].
///
/// Throws a [SendPasswordResetEmailFailureInterface] if an exception occurs.
Future<void> sendPasswordResetEmail({required String email});
/// Sends link to login.
///
/// Throws a [SendSignInLinkEmailFailureInterface] if an exception occurs.
Future<void> sendSignInLinkEmail({required String email});
/// Confirms the password reset with the provided [newPassword] and [code].
///
/// Throws a [ConfirmPasswordResetFailureInterface] if an exception occurs.
Future<void> confirmPasswordReset({
required String code,
required String newPassword,
});
/// Verify password reset code.
///
/// Throws a [VerifyPasswordResetCodeFailureInterface] if an exception occurs.
Future<void> verifyPasswordResetCode({required String code});
/// Signs out the current user which will emit
/// [UserInterface.empty] from the [user] Stream.
Future<void> signOut();
/// Refreshes the current user.
///
/// Throws a [RefreshFailureInterface] if an exception occurs.
Future<void> refresh();
}
@@ -0,0 +1,18 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
export 'authentication_repository_firebase.dart';
export 'authentication_repository_interface.dart';
@@ -0,0 +1,104 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:wyatt_authentication_bloc/src/authentication/cubit/authentication_cubit.dart';
import 'package:wyatt_authentication_bloc/src/form/form_status.dart';
import 'package:wyatt_authentication_bloc/src/form/form_validator.dart';
import 'package:wyatt_authentication_bloc/src/models/email.dart';
import 'package:wyatt_authentication_bloc/src/models/exceptions/exceptions_interface.dart';
import 'package:wyatt_authentication_bloc/src/models/password.dart';
import 'package:wyatt_authentication_bloc/src/repositories/authentication_repository_interface.dart';
part 'sign_in_state.dart';
class SignInCubit extends Cubit<SignInState> {
final AuthenticationRepositoryInterface _authenticationRepository;
final AuthenticationCubit _authenticationCubit;
SignInCubit({
required AuthenticationRepositoryInterface authenticationRepository,
required AuthenticationCubit authenticationCubit,
}) : _authenticationRepository = authenticationRepository,
_authenticationCubit = authenticationCubit,
super(const SignInState());
void emailChanged(String value) {
final Email email = Email.dirty(value);
emit(
state.copyWith(
email: email,
status: FormValidator.validate([email, state.password]),
),
);
}
void passwordChanged(String value) {
final Password password = Password.dirty(value);
emit(
state.copyWith(
password: password,
status: FormValidator.validate([state.email, password]),
),
);
}
Future<void> signInAnonymously() async {
// TODO(hpcl): Maybe check this in UI.
if (state.status.isSubmissionInProgress) {
return;
}
emit(state.copyWith(status: FormStatus.submissionInProgress));
try {
await _authenticationRepository.signInAnonymously();
_authenticationCubit.start();
emit(state.copyWith(status: FormStatus.submissionSuccess));
} on SignInAnonymouslyFailureInterface catch (e) {
emit(
state.copyWith(
errorMessage: e.message,
status: FormStatus.submissionFailure,
),
);
} catch (_) {
emit(state.copyWith(status: FormStatus.submissionFailure));
}
}
Future<void> signInWithEmailAndPassword() async {
if (!state.status.isValidated) return;
emit(state.copyWith(status: FormStatus.submissionInProgress));
try {
await _authenticationRepository.signInWithEmailAndPassword(
email: state.email.value,
password: state.password.value,
);
_authenticationCubit.start();
emit(state.copyWith(status: FormStatus.submissionSuccess));
} on SignInWithEmailAndPasswordFailureInterface catch (e) {
emit(
state.copyWith(
errorMessage: e.message,
status: FormStatus.submissionFailure,
),
);
} catch (_) {
emit(state.copyWith(status: FormStatus.submissionFailure));
}
}
}
@@ -0,0 +1,48 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
part of 'sign_in_cubit.dart';
class SignInState extends Equatable {
final Email email;
final Password password;
final FormStatus status;
final String? errorMessage;
const SignInState({
this.email = const Email.pure(),
this.password = const Password.pure(),
this.status = FormStatus.pure,
this.errorMessage,
});
SignInState copyWith({
Email? email,
Password? password,
FormStatus? status,
String? errorMessage,
}) {
return SignInState(
email: email ?? this.email,
password: password ?? this.password,
status: status ?? this.status,
errorMessage: errorMessage ?? this.errorMessage,
);
}
@override
List<Object> get props => [email, password, status];
}
@@ -0,0 +1,17 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
export 'cubit/sign_in_cubit.dart';
@@ -0,0 +1,166 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:flutter/foundation.dart';
import 'package:wyatt_authentication_bloc/src/authentication/cubit/authentication_cubit.dart';
import 'package:wyatt_authentication_bloc/src/form/form_data.dart';
import 'package:wyatt_authentication_bloc/src/form/form_input.dart';
import 'package:wyatt_authentication_bloc/src/form/form_status.dart';
import 'package:wyatt_authentication_bloc/src/form/form_validator.dart';
import 'package:wyatt_authentication_bloc/src/models/email.dart';
import 'package:wyatt_authentication_bloc/src/models/exceptions/exceptions_interface.dart';
import 'package:wyatt_authentication_bloc/src/models/operations.dart';
import 'package:wyatt_authentication_bloc/src/models/password.dart';
import 'package:wyatt_authentication_bloc/src/models/validation_error.dart';
import 'package:wyatt_authentication_bloc/src/repositories/authentication_repository_interface.dart';
part 'sign_up_state.dart';
class SignUpCubit extends Cubit<SignUpState> {
final AuthenticationRepositoryInterface _authenticationRepository;
final AuthenticationCubit _authenticationCubit;
final Future Function(SignUpState state, String? uid)? _onSignUpSuccess;
SignUpCubit({
required AuthenticationRepositoryInterface authenticationRepository,
required AuthenticationCubit authenticationCubit,
required FormData entries,
Future Function(SignUpState state, String? uid)? onSignUpSuccess,
}) : _authenticationRepository = authenticationRepository,
_authenticationCubit = authenticationCubit,
_onSignUpSuccess = onSignUpSuccess,
super(SignUpState(data: entries));
void emailChanged(String value) {
final Email email = Email.dirty(value);
final List<FormInput<dynamic, ValidationError>> inputsToValidate = [
email,
state.password,
...state.data.inputs<dynamic>(),
];
emit(
state.copyWith(
email: email,
status: FormValidator.validate(inputsToValidate),
),
);
}
void passwordChanged(String value) {
final Password password = Password.dirty(value);
final List<FormInput<dynamic, ValidationError>> inputsToValidate = [
state.email,
password,
...state.data.inputs<dynamic>(),
];
emit(
state.copyWith(
password: password,
status: FormValidator.validate(inputsToValidate),
),
);
}
void dataChanged<T>(String field, FormInput dirtyValue) {
final _form = state.data.clone();
if (_form.contains(field)) {
_form.update(field, dirtyValue);
} else {
throw Exception('Form field $field not found');
}
emit(
state.copyWith(
data: _form,
status: FormValidator.validate(
[state.email, state.password, ..._form.inputs<dynamic>()],
),
),
);
}
void updateFormData(
FormData data, {
SetOperation operation = SetOperation.replace,
}) {
FormData _form = data;
switch (operation) {
case SetOperation.replace:
_form = data;
break;
case SetOperation.difference:
_form = state.data.difference(data);
break;
case SetOperation.intersection:
_form = state.data.intersection(data);
break;
case SetOperation.union:
_form = state.data.union(data);
break;
}
emit(
state.copyWith(
data: _form,
status: FormValidator.validate(
[state.email, state.password, ..._form.inputs<dynamic>()],
),
),
);
}
Future<void> signUpFormSubmitted() async {
if (!state.status.isValidated) return;
emit(state.copyWith(status: FormStatus.submissionInProgress));
try {
final uid = await _authenticationRepository.signUp(
email: state.email.value,
password: state.password.value,
);
await _onSignUpSuccess?.call(state, uid);
if (_authenticationCubit.start()) {
emit(state.copyWith(status: FormStatus.submissionSuccess));
} else {
emit(
state.copyWith(
errorMessage: 'Failed to start authentication cubit',
status: FormStatus.submissionFailure,
),
);
}
} on SignUpWithEmailAndPasswordFailureInterface catch (e) {
emit(
state.copyWith(
errorMessage: e.message,
status: FormStatus.submissionFailure,
),
);
} catch (e) {
debugPrint(e.toString());
emit(state.copyWith(status: FormStatus.submissionFailure));
}
}
}
@@ -0,0 +1,77 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
part of 'sign_up_cubit.dart';
@immutable
class SignUpState {
final Email email;
final Password password;
final FormStatus status;
final FormData data;
final String? errorMessage;
const SignUpState({
this.email = const Email.pure(),
this.password = const Password.pure(),
this.status = FormStatus.pure,
required this.data,
this.errorMessage,
});
SignUpState copyWith({
Email? email,
Password? password,
FormStatus? status,
FormData? data,
String? errorMessage,
}) {
return SignUpState(
email: email ?? this.email,
password: password ?? this.password,
status: status ?? this.status,
data: data ?? this.data,
errorMessage: errorMessage ?? this.errorMessage,
);
}
@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
return other is SignUpState &&
other.email == email &&
other.password == password &&
other.status == status &&
other.data == data &&
other.errorMessage == errorMessage;
}
@override
int get hashCode {
return email.hashCode ^
password.hashCode ^
status.hashCode ^
data.hashCode ^
errorMessage.hashCode;
}
@override
String toString() {
// ignore: lines_longer_than_80_chars
return 'SignUpState(email: $email, password: $password, status: $status, data: $data, errorMessage: $errorMessage)';
}
}
@@ -0,0 +1,17 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
export 'cubit/sign_up_cubit.dart';
@@ -0,0 +1,24 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
export 'authentication/authentication.dart';
export 'email_verification/email_verification.dart';
export 'form/form.dart';
export 'models/models.dart';
export 'password_reset/password_reset.dart';
export 'repositories/repositories.dart';
export 'sign_in/sign_in.dart';
export 'sign_up/sign_up.dart';