Compare commits

...
118 changed files with 759 additions and 592 deletions
+28
View File
@@ -3,6 +3,34 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## 2022-12-13
### Changes
---
Packages with breaking changes:
- There are no breaking changes in this release.
Packages with other changes:
- [`wyatt_authentication_bloc` - `v0.4.1`](#wyatt_authentication_bloc---v041)
---
#### `wyatt_authentication_bloc` - `v0.4.1`
- **REFACTOR**: use fromFirebaseUser factory to parse user.
- **REFACTOR**: update deps and re-export them.
- **FIX**: fix mock on register.
- **FEAT**: add google sign in in signIn cubit.
- **FEAT**: add google sign in parameter in firebase data source constructor.
- **FEAT**: add google sign_in support (closes #59).
- **FEAT**: add reauthenticate, updateEmail and updatePassword.
- **DOCS**: add correct header.
## 2022-12-12 ## 2022-12-12
### Changes ### Changes
@@ -24,9 +24,8 @@ import 'package:wyatt_http_client/wyatt_http_client.dart';
import 'package:wyatt_type_utils/wyatt_type_utils.dart'; import 'package:wyatt_type_utils/wyatt_type_utils.dart';
class AlbumApiDataSourceImpl extends AlbumRemoteDataSource { class AlbumApiDataSourceImpl extends AlbumRemoteDataSource {
final MiddlewareClient _client;
AlbumApiDataSourceImpl(this._client); AlbumApiDataSourceImpl(this._client);
final MiddlewareClient _client;
@override @override
Future<Album> getAlbum(int id) async { Future<Album> getAlbum(int id) async {
@@ -24,9 +24,8 @@ import 'package:wyatt_http_client/wyatt_http_client.dart';
import 'package:wyatt_type_utils/wyatt_type_utils.dart'; import 'package:wyatt_type_utils/wyatt_type_utils.dart';
class PhotoApiDataSourceImpl extends PhotoRemoteDataSource { class PhotoApiDataSourceImpl extends PhotoRemoteDataSource {
final MiddlewareClient _client;
PhotoApiDataSourceImpl(this._client); PhotoApiDataSourceImpl(this._client);
final MiddlewareClient _client;
@override @override
Future<Photo> getPhoto(int id) async { Future<Photo> getPhoto(int id) async {
@@ -24,15 +24,15 @@ import 'package:wyatt_architecture/wyatt_architecture.dart';
import 'package:wyatt_type_utils/wyatt_type_utils.dart'; import 'package:wyatt_type_utils/wyatt_type_utils.dart';
class PhotoRepositoryImpl extends PhotoRepository { class PhotoRepositoryImpl extends PhotoRepository {
final PhotoRemoteDataSource _photoRemoteDataSource;
final AlbumRemoteDataSource _albumRemoteDataSource;
final FavoriteLocalDataSource _favoriteLocalDataSource;
PhotoRepositoryImpl( PhotoRepositoryImpl(
this._photoRemoteDataSource, this._photoRemoteDataSource,
this._albumRemoteDataSource, this._albumRemoteDataSource,
this._favoriteLocalDataSource, this._favoriteLocalDataSource,
); );
final PhotoRemoteDataSource _photoRemoteDataSource;
final AlbumRemoteDataSource _albumRemoteDataSource;
final FavoriteLocalDataSource _favoriteLocalDataSource;
@override @override
FutureOrResult<void> addPhotoToFavorites(Photo photo) => Result.tryCatchAsync( FutureOrResult<void> addPhotoToFavorites(Photo photo) => Result.tryCatchAsync(
@@ -21,9 +21,9 @@ import 'package:architecture_example/domain/repositories/photo_repository.dart';
import 'package:wyatt_architecture/wyatt_architecture.dart'; import 'package:wyatt_architecture/wyatt_architecture.dart';
class AddPhotoToFavorites extends AsyncUseCase<Photo, List<Photo>> { class AddPhotoToFavorites extends AsyncUseCase<Photo, List<Photo>> {
final PhotoRepository _photoRepository;
AddPhotoToFavorites(this._photoRepository); AddPhotoToFavorites(this._photoRepository);
final PhotoRepository _photoRepository;
@override @override
FutureOrResult<List<Photo>> execute(Photo? params) async { FutureOrResult<List<Photo>> execute(Photo? params) async {
@@ -20,9 +20,9 @@ import 'package:architecture_example/domain/repositories/photo_repository.dart';
import 'package:wyatt_architecture/wyatt_architecture.dart'; import 'package:wyatt_architecture/wyatt_architecture.dart';
class CheckIfPhotoIsInFavorites extends AsyncUseCase<int, bool> { class CheckIfPhotoIsInFavorites extends AsyncUseCase<int, bool> {
final PhotoRepository _photoRepository;
CheckIfPhotoIsInFavorites(this._photoRepository); CheckIfPhotoIsInFavorites(this._photoRepository);
final PhotoRepository _photoRepository;
@override @override
FutureOrResult<bool> execute(int? params) async => FutureOrResult<bool> execute(int? params) async =>
@@ -19,9 +19,9 @@ import 'package:architecture_example/domain/repositories/photo_repository.dart';
import 'package:wyatt_architecture/wyatt_architecture.dart'; import 'package:wyatt_architecture/wyatt_architecture.dart';
class DisplayFavorites extends AsyncUseCase<NoParam, List<Photo>> { class DisplayFavorites extends AsyncUseCase<NoParam, List<Photo>> {
final PhotoRepository _photoRepository;
DisplayFavorites(this._photoRepository); DisplayFavorites(this._photoRepository);
final PhotoRepository _photoRepository;
@override @override
FutureOrResult<List<Photo>> execute(void params) { FutureOrResult<List<Photo>> execute(void params) {
@@ -21,9 +21,9 @@ import 'package:architecture_example/domain/repositories/photo_repository.dart';
import 'package:wyatt_architecture/wyatt_architecture.dart'; import 'package:wyatt_architecture/wyatt_architecture.dart';
class DisplayPhoto extends AsyncUseCase<int, Photo> { class DisplayPhoto extends AsyncUseCase<int, Photo> {
final PhotoRepository _photoRepository;
DisplayPhoto(this._photoRepository); DisplayPhoto(this._photoRepository);
final PhotoRepository _photoRepository;
@override @override
FutureOrResult<Photo> execute(int? params) { FutureOrResult<Photo> execute(int? params) {
@@ -22,9 +22,9 @@ import 'package:architecture_example/domain/usecases/photos/params/query_paramet
import 'package:wyatt_architecture/wyatt_architecture.dart'; import 'package:wyatt_architecture/wyatt_architecture.dart';
class OpenAlbum extends AsyncUseCase<QueryParameters, List<Photo>> { class OpenAlbum extends AsyncUseCase<QueryParameters, List<Photo>> {
final PhotoRepository _photoRepository;
OpenAlbum(this._photoRepository); OpenAlbum(this._photoRepository);
final PhotoRepository _photoRepository;
@override @override
FutureOrResult<List<Photo>> execute(QueryParameters? params) { FutureOrResult<List<Photo>> execute(QueryParameters? params) {
@@ -15,9 +15,9 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
class QueryParameters { class QueryParameters {
QueryParameters(this.start, this.limit, {this.albumId = -1});
final int albumId; final int albumId;
final int? start; final int? start;
final int? limit; final int? limit;
QueryParameters(this.start, this.limit, {this.albumId = -1});
} }
@@ -21,9 +21,9 @@ import 'package:architecture_example/domain/repositories/photo_repository.dart';
import 'package:wyatt_architecture/wyatt_architecture.dart'; import 'package:wyatt_architecture/wyatt_architecture.dart';
class RemovePhotoFromFavorites extends AsyncUseCase<int, List<Photo>> { class RemovePhotoFromFavorites extends AsyncUseCase<int, List<Photo>> {
final PhotoRepository _photoRepository;
RemovePhotoFromFavorites(this._photoRepository); RemovePhotoFromFavorites(this._photoRepository);
final PhotoRepository _photoRepository;
@override @override
FutureOrResult<List<Photo>> execute(int? params) async { FutureOrResult<List<Photo>> execute(int? params) async {
@@ -22,9 +22,9 @@ import 'package:architecture_example/domain/usecases/photos/params/query_paramet
import 'package:wyatt_architecture/wyatt_architecture.dart'; import 'package:wyatt_architecture/wyatt_architecture.dart';
class RetrieveAllAlbums extends AsyncUseCase<QueryParameters, List<Album>> { class RetrieveAllAlbums extends AsyncUseCase<QueryParameters, List<Album>> {
final PhotoRepository _photoRepository;
RetrieveAllAlbums(this._photoRepository); RetrieveAllAlbums(this._photoRepository);
final PhotoRepository _photoRepository;
@override @override
FutureOrResult<List<Album>> execute(QueryParameters? params) { FutureOrResult<List<Album>> execute(QueryParameters? params) {
@@ -33,7 +33,6 @@ EventTransformer<E> throttleDroppable<E>(Duration duration) =>
(events, mapper) => droppable<E>().call(events.throttle(duration), mapper); (events, mapper) => droppable<E>().call(events.throttle(duration), mapper);
class AlbumBloc extends Bloc<AlbumEvent, AlbumState> { class AlbumBloc extends Bloc<AlbumEvent, AlbumState> {
final RetrieveAllAlbums _retrieveAllAlbums;
AlbumBloc(this._retrieveAllAlbums) : super(const AlbumState()) { AlbumBloc(this._retrieveAllAlbums) : super(const AlbumState()) {
on<AlbumFetched>( on<AlbumFetched>(
@@ -41,6 +40,7 @@ class AlbumBloc extends Bloc<AlbumEvent, AlbumState> {
transformer: throttleDroppable(throttleDuration), transformer: throttleDroppable(throttleDuration),
); );
} }
final RetrieveAllAlbums _retrieveAllAlbums;
Future<void> _onAlbumFetched( Future<void> _onAlbumFetched(
AlbumFetched event, AlbumFetched event,
@@ -26,19 +26,19 @@ abstract class PhotoDetailsState extends Equatable {
class PhotoDetailsInitial extends PhotoDetailsState {} class PhotoDetailsInitial extends PhotoDetailsState {}
class PhotoDetailsSuccess extends PhotoDetailsState { class PhotoDetailsSuccess extends PhotoDetailsState {
final Photo photo;
final bool isFavorite;
const PhotoDetailsSuccess(this.photo, {required this.isFavorite}); const PhotoDetailsSuccess(this.photo, {required this.isFavorite});
final Photo photo;
final bool isFavorite;
@override @override
List<Object> get props => [photo, isFavorite]; List<Object> get props => [photo, isFavorite];
} }
class PhotoDetailsFailure extends PhotoDetailsState { class PhotoDetailsFailure extends PhotoDetailsState {
final String error;
const PhotoDetailsFailure(this.error); const PhotoDetailsFailure(this.error);
final String error;
@override @override
List<Object> get props => [error]; List<Object> get props => [error];
@@ -23,10 +23,10 @@ import 'package:flutter_bloc/flutter_bloc.dart';
part 'favorite_checker_state.dart'; part 'favorite_checker_state.dart';
class FavoriteCheckerCubit extends Cubit<FavoriteCheckerState> { class FavoriteCheckerCubit extends Cubit<FavoriteCheckerState> {
final CheckIfPhotoIsInFavorites _checkIfPhotoIsInFavorites;
FavoriteCheckerCubit(this._checkIfPhotoIsInFavorites) FavoriteCheckerCubit(this._checkIfPhotoIsInFavorites)
: super(FavoriteCheckerInitial()); : super(FavoriteCheckerInitial());
final CheckIfPhotoIsInFavorites _checkIfPhotoIsInFavorites;
FutureOr<void> checkIfPhotoIsInFavorites(int photoId) async { FutureOr<void> checkIfPhotoIsInFavorites(int photoId) async {
final response = await _checkIfPhotoIsInFavorites.call(photoId); final response = await _checkIfPhotoIsInFavorites.call(photoId);
@@ -10,19 +10,19 @@ abstract class FavoriteCheckerState extends Equatable {
class FavoriteCheckerInitial extends FavoriteCheckerState {} class FavoriteCheckerInitial extends FavoriteCheckerState {}
class FavoriteCheckerSuccess extends FavoriteCheckerState { class FavoriteCheckerSuccess extends FavoriteCheckerState {
final int photoId;
final bool isFavorite;
const FavoriteCheckerSuccess(this.photoId, {required this.isFavorite}); const FavoriteCheckerSuccess(this.photoId, {required this.isFavorite});
final int photoId;
final bool isFavorite;
@override @override
List<Object> get props => [photoId, isFavorite]; List<Object> get props => [photoId, isFavorite];
} }
class FavoriteCheckerFailure extends FavoriteCheckerState { class FavoriteCheckerFailure extends FavoriteCheckerState {
final String error;
const FavoriteCheckerFailure(this.error); const FavoriteCheckerFailure(this.error);
final String error;
@override @override
List<Object> get props => [error]; List<Object> get props => [error];
@@ -33,7 +33,6 @@ EventTransformer<E> throttleDroppable<E>(Duration duration) =>
(events, mapper) => droppable<E>().call(events.throttle(duration), mapper); (events, mapper) => droppable<E>().call(events.throttle(duration), mapper);
class PhotoBloc extends Bloc<PhotoEvent, PhotoState> { class PhotoBloc extends Bloc<PhotoEvent, PhotoState> {
final OpenAlbum _openAlbum;
PhotoBloc(this._openAlbum) : super(const PhotoState()) { PhotoBloc(this._openAlbum) : super(const PhotoState()) {
on<PhotoFetched>( on<PhotoFetched>(
@@ -41,6 +40,7 @@ class PhotoBloc extends Bloc<PhotoEvent, PhotoState> {
transformer: throttleDroppable(throttleDuration), transformer: throttleDroppable(throttleDuration),
); );
} }
final OpenAlbum _openAlbum;
Future<void> _onPhotoFetched( Future<void> _onPhotoFetched(
PhotoFetched event, PhotoFetched event,
@@ -24,7 +24,7 @@ abstract class PhotoEvent extends Equatable {
} }
class PhotoFetched extends PhotoEvent { class PhotoFetched extends PhotoEvent {
final int albumId;
const PhotoFetched(this.albumId); const PhotoFetched(this.albumId);
final int albumId;
} }
@@ -17,9 +17,9 @@
import 'package:wyatt_type_utils/wyatt_type_utils.dart'; import 'package:wyatt_type_utils/wyatt_type_utils.dart';
abstract class AppException implements Exception { abstract class AppException implements Exception {
final String? message;
AppException([this.message]); AppException([this.message]);
final String? message;
@override @override
String toString() { String toString() {
@@ -1,3 +1,14 @@
## 0.4.1
- **REFACTOR**: use fromFirebaseUser factory to parse user.
- **REFACTOR**: update deps and re-export them.
- **FIX**: fix mock on register.
- **FEAT**: add google sign in in signIn cubit.
- **FEAT**: add google sign in parameter in firebase data source constructor.
- **FEAT**: add google sign_in support (closes #59).
- **FEAT**: add reauthenticate, updateEmail and updatePassword.
- **DOCS**: add correct header.
## 0.4.0+3 ## 0.4.0+3
- Update a dependency to the latest release. - Update a dependency to the latest release.
@@ -1,4 +1 @@
include: package:wyatt_analysis/analysis_options.flutter.yaml include: package:wyatt_analysis/analysis_options.flutter.yaml
analyzer:
exclude: "!example/**"
@@ -45,5 +45,3 @@ app.*.map.json
/android/app/debug /android/app/debug
/android/app/profile /android/app/profile
/android/app/release /android/app/release
firebase_options.dart
@@ -4,7 +4,7 @@
* ----- * -----
* File: launch.json * File: launch.json
* Created Date: 19/08/2022 15:12:25 * Created Date: 19/08/2022 15:12:25
* Last Modified: 19/08/2022 15:22:02 * Last Modified: Tue Dec 13 2022
* ----- * -----
* Copyright (c) 2022 * Copyright (c) 2022
*/ */
@@ -15,11 +15,18 @@
"version": "0.2.0", "version": "0.2.0",
"configurations": [ "configurations": [
{ {
"name": "example_router", "name": "Mock",
"request": "launch", "request": "launch",
"type": "dart", "type": "dart",
"program": "lib/main.dart", "program": "lib/main.dart",
"flutterMode": "debug" "flutterMode": "debug"
},
{
"name": "Firebase",
"request": "launch",
"type": "dart",
"program": "lib/main_firebase.dart",
"flutterMode": "debug"
} }
] ]
} }
@@ -5,41 +5,30 @@ PODS:
- AppAuth/Core (1.6.0) - AppAuth/Core (1.6.0)
- AppAuth/ExternalUserAgent (1.6.0): - AppAuth/ExternalUserAgent (1.6.0):
- AppAuth/Core - AppAuth/Core
- FBAEMKit (14.1.0): - Firebase/Auth (10.3.0):
- FBSDKCoreKit_Basics (= 14.1.0)
- FBSDKCoreKit (14.1.0):
- FBAEMKit (= 14.1.0)
- FBSDKCoreKit_Basics (= 14.1.0)
- FBSDKCoreKit_Basics (14.1.0)
- FBSDKLoginKit (14.1.0):
- FBSDKCoreKit (= 14.1.0)
- Firebase/Auth (10.0.0):
- Firebase/CoreOnly - Firebase/CoreOnly
- FirebaseAuth (~> 10.0.0) - FirebaseAuth (~> 10.3.0)
- Firebase/CoreOnly (10.0.0): - Firebase/CoreOnly (10.3.0):
- FirebaseCore (= 10.0.0) - FirebaseCore (= 10.3.0)
- firebase_auth (4.1.1): - firebase_auth (4.2.0):
- Firebase/Auth (= 10.0.0) - Firebase/Auth (= 10.3.0)
- firebase_core - firebase_core
- Flutter - Flutter
- firebase_core (2.1.1): - firebase_core (2.4.0):
- Firebase/CoreOnly (= 10.0.0) - Firebase/CoreOnly (= 10.3.0)
- Flutter - Flutter
- FirebaseAuth (10.0.0): - FirebaseAuth (10.3.0):
- FirebaseCore (~> 10.0) - FirebaseCore (~> 10.0)
- GoogleUtilities/AppDelegateSwizzler (~> 7.8) - GoogleUtilities/AppDelegateSwizzler (~> 7.8)
- GoogleUtilities/Environment (~> 7.8) - GoogleUtilities/Environment (~> 7.8)
- GTMSessionFetcher/Core (~> 2.1) - GTMSessionFetcher/Core (< 4.0, >= 2.1)
- FirebaseCore (10.0.0): - FirebaseCore (10.3.0):
- FirebaseCoreInternal (~> 10.0) - FirebaseCoreInternal (~> 10.0)
- GoogleUtilities/Environment (~> 7.8) - GoogleUtilities/Environment (~> 7.8)
- GoogleUtilities/Logger (~> 7.8) - GoogleUtilities/Logger (~> 7.8)
- FirebaseCoreInternal (10.1.0): - FirebaseCoreInternal (10.3.0):
- "GoogleUtilities/NSData+zlib (~> 7.8)" - "GoogleUtilities/NSData+zlib (~> 7.8)"
- Flutter (1.0.0) - Flutter (1.0.0)
- flutter_facebook_auth (4.4.1):
- FBSDKLoginKit (= 14.1.0)
- Flutter
- google_sign_in_ios (0.0.1): - google_sign_in_ios (0.0.1):
- Flutter - Flutter
- GoogleSignIn (~> 6.2) - GoogleSignIn (~> 6.2)
@@ -47,47 +36,36 @@ PODS:
- AppAuth (~> 1.5) - AppAuth (~> 1.5)
- GTMAppAuth (~> 1.3) - GTMAppAuth (~> 1.3)
- GTMSessionFetcher/Core (< 3.0, >= 1.1) - GTMSessionFetcher/Core (< 3.0, >= 1.1)
- GoogleUtilities/AppDelegateSwizzler (7.8.0): - GoogleUtilities/AppDelegateSwizzler (7.10.0):
- GoogleUtilities/Environment - GoogleUtilities/Environment
- GoogleUtilities/Logger - GoogleUtilities/Logger
- GoogleUtilities/Network - GoogleUtilities/Network
- GoogleUtilities/Environment (7.8.0): - GoogleUtilities/Environment (7.10.0):
- PromisesObjC (< 3.0, >= 1.2) - PromisesObjC (< 3.0, >= 1.2)
- GoogleUtilities/Logger (7.8.0): - GoogleUtilities/Logger (7.10.0):
- GoogleUtilities/Environment - GoogleUtilities/Environment
- GoogleUtilities/Network (7.8.0): - GoogleUtilities/Network (7.10.0):
- GoogleUtilities/Logger - GoogleUtilities/Logger
- "GoogleUtilities/NSData+zlib" - "GoogleUtilities/NSData+zlib"
- GoogleUtilities/Reachability - GoogleUtilities/Reachability
- "GoogleUtilities/NSData+zlib (7.8.0)" - "GoogleUtilities/NSData+zlib (7.10.0)"
- GoogleUtilities/Reachability (7.8.0): - GoogleUtilities/Reachability (7.10.0):
- GoogleUtilities/Logger - GoogleUtilities/Logger
- GTMAppAuth (1.3.1): - GTMAppAuth (1.3.1):
- AppAuth/Core (~> 1.6) - AppAuth/Core (~> 1.6)
- GTMSessionFetcher/Core (< 3.0, >= 1.5) - GTMSessionFetcher/Core (< 3.0, >= 1.5)
- GTMSessionFetcher/Core (2.1.0) - GTMSessionFetcher/Core (2.3.0)
- PromisesObjC (2.1.1) - PromisesObjC (2.1.1)
- sign_in_with_apple (0.0.1):
- Flutter
- twitter_login (0.0.1):
- Flutter
DEPENDENCIES: DEPENDENCIES:
- firebase_auth (from `.symlinks/plugins/firebase_auth/ios`) - firebase_auth (from `.symlinks/plugins/firebase_auth/ios`)
- firebase_core (from `.symlinks/plugins/firebase_core/ios`) - firebase_core (from `.symlinks/plugins/firebase_core/ios`)
- Flutter (from `Flutter`) - Flutter (from `Flutter`)
- flutter_facebook_auth (from `.symlinks/plugins/flutter_facebook_auth/ios`)
- google_sign_in_ios (from `.symlinks/plugins/google_sign_in_ios/ios`) - google_sign_in_ios (from `.symlinks/plugins/google_sign_in_ios/ios`)
- sign_in_with_apple (from `.symlinks/plugins/sign_in_with_apple/ios`)
- twitter_login (from `.symlinks/plugins/twitter_login/ios`)
SPEC REPOS: SPEC REPOS:
trunk: trunk:
- AppAuth - AppAuth
- FBAEMKit
- FBSDKCoreKit
- FBSDKCoreKit_Basics
- FBSDKLoginKit
- Firebase - Firebase
- FirebaseAuth - FirebaseAuth
- FirebaseCore - FirebaseCore
@@ -105,37 +83,24 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/firebase_core/ios" :path: ".symlinks/plugins/firebase_core/ios"
Flutter: Flutter:
:path: Flutter :path: Flutter
flutter_facebook_auth:
:path: ".symlinks/plugins/flutter_facebook_auth/ios"
google_sign_in_ios: google_sign_in_ios:
:path: ".symlinks/plugins/google_sign_in_ios/ios" :path: ".symlinks/plugins/google_sign_in_ios/ios"
sign_in_with_apple:
:path: ".symlinks/plugins/sign_in_with_apple/ios"
twitter_login:
:path: ".symlinks/plugins/twitter_login/ios"
SPEC CHECKSUMS: SPEC CHECKSUMS:
AppAuth: 8fca6b5563a5baef2c04bee27538025e4ceb2add AppAuth: 8fca6b5563a5baef2c04bee27538025e4ceb2add
FBAEMKit: a899515e45476027f73aef377b5cffadcd56ca3a Firebase: f92fc551ead69c94168d36c2b26188263860acd9
FBSDKCoreKit: 24f8bc8d3b5b2a8c5c656a1329492a12e8efa792 firebase_auth: 579a0dc15451491cc83fccaa5102296635f24938
FBSDKCoreKit_Basics: 6e578c9bdc7aa1365dbbbde633c9ebb536bcaa98 firebase_core: 6f2f753e316765799d88568232ed59e300ff53db
FBSDKLoginKit: 787de205d524c3a4b17d527916f1d066e4361660 FirebaseAuth: 0e415d29d846c1dce2fb641e46f35e9888d9bec6
Firebase: 1b810f3d0c0532e27a48f1961f8c0400a668a2cf FirebaseCore: 988754646ab3bd4bdcb740f1bfe26b9f6c0d5f2a
firebase_auth: dd33e93fce72a1c72040f7380dacf06e89db5705 FirebaseCoreInternal: 29b76f784d607df8b2a1259d73c3f04f1210137b
firebase_core: 5c0bb0ca7d0e70480a68a6e9ad9bf55d1edd5305
FirebaseAuth: 493382cf533cc45e2862b00e9aa4cfe4c98daf71
FirebaseCore: 97f48a3a567a72b8d4daa0f03c3aadb78df4e995
FirebaseCoreInternal: 96d75228e10fd369564da51bd898414eb0f54df5
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
flutter_facebook_auth: 361ac7a57263ebf327f26089507ead0d66558ee8
google_sign_in_ios: 4f85eb9f937450765c8573bb85fd8cd6a5af675c google_sign_in_ios: 4f85eb9f937450765c8573bb85fd8cd6a5af675c
GoogleSignIn: 5651ce3a61e56ca864160e79b484cd9ed3f49b7a GoogleSignIn: 5651ce3a61e56ca864160e79b484cd9ed3f49b7a
GoogleUtilities: 1d20a6ad97ef46f67bbdec158ce00563a671ebb7 GoogleUtilities: bad72cb363809015b1f7f19beb1f1cd23c589f95
GTMAppAuth: 0ff230db599948a9ad7470ca667337803b3fc4dd GTMAppAuth: 0ff230db599948a9ad7470ca667337803b3fc4dd
GTMSessionFetcher: ffbb25ec00ebcb5201adab0a56d808f6f1902d9f GTMSessionFetcher: 3a63d75eecd6aa32c2fc79f578064e1214dfdec2
PromisesObjC: ab77feca74fa2823e7af4249b8326368e61014cb PromisesObjC: ab77feca74fa2823e7af4249b8326368e61014cb
sign_in_with_apple: f3bf75217ea4c2c8b91823f225d70230119b8440
twitter_login: 2794db69b7640681171b17b3c2c84ad9dfb4a57f
PODFILE CHECKSUM: ef19549a9bc3046e7bb7d2fab4d021637c0c58a3 PODFILE CHECKSUM: ef19549a9bc3046e7bb7d2fab4d021637c0c58a3
@@ -28,6 +28,17 @@
<string>LaunchScreen</string> <string>LaunchScreen</string>
<key>UIMainStoryboardFile</key> <key>UIMainStoryboardFile</key>
<string>Main</string> <string>Main</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLSchemes</key>
<array>
<string>com.googleusercontent.apps.405351917235-2jv4ff02kovoim58f8d6d0rsa14apgkj</string>
</array>
</dict>
</array>
<key>UISupportedInterfaceOrientations</key> <key>UISupportedInterfaceOrientations</key>
<array> <array>
<string>UIInterfaceOrientationPortrait</string> <string>UIInterfaceOrientationPortrait</string>
@@ -1,7 +1,7 @@
{ {
"file_generated_by": "FlutterFire CLI", "file_generated_by": "FlutterFire CLI",
"purpose": "FirebaseAppID & ProjectID for this Firebase app in this directory", "purpose": "FirebaseAppID & ProjectID for this Firebase app in this directory",
"GOOGLE_APP_ID": "1:136771801992:ios:bcdca68d2b7d227097203d", "GOOGLE_APP_ID": "1:405351917235:ios:869f0ad8ace08db899f2c6",
"FIREBASE_PROJECT_ID": "tchat-beta", "FIREBASE_PROJECT_ID": "meerabel-dev",
"GCM_SENDER_ID": "136771801992" "GCM_SENDER_ID": "405351917235"
} }
@@ -18,9 +18,47 @@ import 'dart:async';
import 'package:example_router/core/dependency_injection/get_it.dart'; import 'package:example_router/core/dependency_injection/get_it.dart';
import 'package:example_router/core/utils/app_bloc_observer.dart'; import 'package:example_router/core/utils/app_bloc_observer.dart';
import 'package:example_router/firebase_options.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
class MockSettings {
static MockSettings? _instance;
/// Data source mode
late bool enable = false;
MockSettings._(this.enable);
factory MockSettings.enable() {
_instance ??= MockSettings._(true);
if (_instance!.enable != true) {
throw Exception('Mock already initialized in: ${_instance!.enable}');
}
return _instance!;
}
factory MockSettings.disable() {
_instance ??= MockSettings._(false);
if (_instance!.enable != false) {
throw Exception('Mock already initialized in: ${_instance!.enable}');
}
return _instance!;
}
static bool isEnable() {
if (_instance == null) {
throw Exception('MockSettings not initialized!');
}
return _instance!.enable == true;
}
static bool isDisable() {
return !isEnable();
}
}
Future<void> bootstrap(FutureOr<Widget> Function() builder) async { Future<void> bootstrap(FutureOr<Widget> Function() builder) async {
await runZonedGuarded( await runZonedGuarded(
() async { () async {
@@ -30,6 +68,12 @@ Future<void> bootstrap(FutureOr<Widget> Function() builder) async {
FlutterError.onError = (details) { FlutterError.onError = (details) {
debugPrint(details.toString()); debugPrint(details.toString());
}; };
if (MockSettings.isDisable()) {
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
}
await GetItInitializer.init(); await GetItInitializer.init();
runApp(await builder()); runApp(await builder());
@@ -14,6 +14,8 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:example_router/bootstrap.dart';
import 'package:example_router/firebase_options.dart';
import 'package:get_it/get_it.dart'; import 'package:get_it/get_it.dart';
import 'package:wyatt_authentication_bloc/wyatt_authentication_bloc.dart'; import 'package:wyatt_authentication_bloc/wyatt_authentication_bloc.dart';
import 'package:wyatt_type_utils/wyatt_type_utils.dart'; import 'package:wyatt_type_utils/wyatt_type_utils.dart';
@@ -24,7 +26,8 @@ abstract class GetItInitializer {
static Future<void> init() async { static Future<void> init() async {
getIt getIt
..registerLazySingleton<AuthenticationRemoteDataSource>( ..registerLazySingleton<AuthenticationRemoteDataSource>(
() => AuthenticationMockDataSourceImpl(registeredAccounts: [ MockSettings.isEnable()
? () => AuthenticationMockDataSourceImpl(registeredAccounts: [
Pair( Pair(
AccountModel( AccountModel(
uid: '1', uid: '1',
@@ -45,7 +48,10 @@ abstract class GetItInitializer {
), ),
'tata1234', 'tata1234',
), ),
]), ])
: () => AuthenticationFirebaseDataSourceImpl(
googleSignIn: GoogleSignIn(
clientId: DefaultFirebaseOptions.ios.iosClientId)),
) )
..registerLazySingleton<AuthenticationCacheDataSource<int>>( ..registerLazySingleton<AuthenticationCacheDataSource<int>>(
() => AuthenticationCacheDataSourceImpl<int>(), () => AuthenticationCacheDataSourceImpl<int>(),
@@ -0,0 +1,67 @@
// File generated by FlutterFire CLI.
// ignore_for_file: lines_longer_than_80_chars, avoid_classes_with_only_static_members
import 'package:firebase_core/firebase_core.dart' show FirebaseOptions;
import 'package:flutter/foundation.dart'
show defaultTargetPlatform, kIsWeb, TargetPlatform;
/// Default [FirebaseOptions] for use with your Firebase apps.
///
/// Example:
/// ```dart
/// import 'firebase_options.dart';
/// // ...
/// await Firebase.initializeApp(
/// options: DefaultFirebaseOptions.currentPlatform,
/// );
/// ```
class DefaultFirebaseOptions {
static FirebaseOptions get currentPlatform {
if (kIsWeb) {
throw UnsupportedError(
'DefaultFirebaseOptions have not been configured for web - '
'you can reconfigure this by running the FlutterFire CLI again.',
);
}
switch (defaultTargetPlatform) {
case TargetPlatform.android:
throw UnsupportedError(
'DefaultFirebaseOptions have not been configured for android - '
'you can reconfigure this by running the FlutterFire CLI again.',
);
case TargetPlatform.iOS:
return ios;
case TargetPlatform.macOS:
throw UnsupportedError(
'DefaultFirebaseOptions have not been configured for macos - '
'you can reconfigure this by running the FlutterFire CLI again.',
);
case TargetPlatform.windows:
throw UnsupportedError(
'DefaultFirebaseOptions have not been configured for windows - '
'you can reconfigure this by running the FlutterFire CLI again.',
);
case TargetPlatform.linux:
throw UnsupportedError(
'DefaultFirebaseOptions have not been configured for linux - '
'you can reconfigure this by running the FlutterFire CLI again.',
);
default:
throw UnsupportedError(
'DefaultFirebaseOptions are not supported for this platform.',
);
}
}
static const FirebaseOptions ios = FirebaseOptions(
apiKey: 'AIzaSyDDmtf0KN7Xw12_pqUsxoBfAxMuvCMmMmk',
appId: '1:405351917235:ios:869f0ad8ace08db899f2c6',
messagingSenderId: '405351917235',
projectId: 'meerabel-dev',
storageBucket: 'meerabel-dev.appspot.com',
androidClientId:
'405351917235-4g1dh3475tq6t1sa2qoh7ol60nf4ta05.apps.googleusercontent.com',
iosClientId:
'405351917235-2jv4ff02kovoim58f8d6d0rsa14apgkj.apps.googleusercontent.com',
iosBundleId: 'com.example.exampleRouter',
);
}
@@ -18,5 +18,6 @@ import 'package:example_router/bootstrap.dart';
import 'package:example_router/presentation/features/app/app.dart'; import 'package:example_router/presentation/features/app/app.dart';
void main() { void main() {
MockSettings.enable();
bootstrap(App.new); bootstrap(App.new);
} }
@@ -14,4 +14,10 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
export 'cryptography.dart'; import 'package:example_router/bootstrap.dart';
import 'package:example_router/presentation/features/app/app.dart';
void main() {
MockSettings.disable();
bootstrap(App.new);
}
@@ -94,6 +94,22 @@ class _SignInAnonymouslyButton extends StatelessWidget {
} }
} }
class _SignInWithGoogleButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
return SubmitBuilder<SignInCubit<int>>(
builder: ((context, cubit, status) {
return status.isSubmissionInProgress
? const CircularProgressIndicator()
: ElevatedButton(
onPressed: () => cubit.signInWithGoogle(),
child: const Text('Sign in Google'),
);
}),
);
}
}
class SignInForm extends StatelessWidget { class SignInForm extends StatelessWidget {
const SignInForm({Key? key}) : super(key: key); const SignInForm({Key? key}) : super(key: key);
@@ -116,6 +132,8 @@ class SignInForm extends StatelessWidget {
_SignInButton(), _SignInButton(),
const SizedBox(height: 16), const SizedBox(height: 16),
_SignInAnonymouslyButton(), _SignInAnonymouslyButton(),
const SizedBox(height: 16),
_SignInWithGoogleButton(),
], ],
), ),
), ),
@@ -30,10 +30,12 @@ class SubPage extends StatelessWidget {
title: const Text('Sub'), title: const Text('Sub'),
actions: [ actions: [
IconButton( IconButton(
onPressed: () => context.read<AuthenticationCubit<int>>().signOut(), onPressed: () =>
context.read<AuthenticationCubit<int>>().signOut(),
icon: const Icon(Icons.logout_rounded)), icon: const Icon(Icons.logout_rounded)),
IconButton( IconButton(
onPressed: () => context.read<AuthenticationRepository<int>>().refresh(), onPressed: () =>
context.read<AuthenticationRepository<int>>().refresh(),
icon: const Icon(Icons.refresh)) icon: const Icon(Icons.refresh))
], ],
), ),
@@ -18,4 +18,3 @@ export 'constants/form_field.dart';
export 'constants/form_name.dart'; export 'constants/form_name.dart';
export 'enums/enums.dart'; export 'enums/enums.dart';
export 'exceptions/exceptions.dart'; export 'exceptions/exceptions.dart';
export 'utils/utils.dart';
@@ -20,15 +20,14 @@ part 'exceptions_firebase.dart';
abstract class AuthenticationFailureInterface extends AppException abstract class AuthenticationFailureInterface extends AppException
implements Exception { implements Exception {
AuthenticationFailureInterface(this.code, this.msg);
AuthenticationFailureInterface.fromCode(this.code)
: msg = 'An unknown error occurred.';
String code; String code;
String msg; String msg;
@override @override
String get message => msg; String get message => msg;
AuthenticationFailureInterface(this.code, this.msg);
AuthenticationFailureInterface.fromCode(this.code)
: msg = 'An unknown error occurred.';
} }
/// {@template apply_action_code_failure} /// {@template apply_action_code_failure}
@@ -277,3 +276,10 @@ abstract class UpdatePasswordFailureInterface
UpdatePasswordFailureInterface.fromCode(super.code) : super.fromCode(); UpdatePasswordFailureInterface.fromCode(super.code) : super.fromCode();
} }
abstract class ModelParsingFailureInterface
extends AuthenticationFailureInterface {
ModelParsingFailureInterface(super.code, super.msg);
ModelParsingFailureInterface.fromCode(super.code) : super.fromCode();
}
@@ -349,3 +349,10 @@ class UpdatePasswordFailureFirebase extends UpdatePasswordFailureInterface {
} }
} }
} }
class ModelParsingFailureFirebase extends ModelParsingFailureInterface {
ModelParsingFailureFirebase([String? code, String? msg])
: super(code ?? 'unknown', msg ?? 'An unknown error occurred.');
ModelParsingFailureFirebase.fromCode(super.code) : super.fromCode();
}
@@ -1,39 +0,0 @@
// 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:convert';
import 'dart:math';
import 'package:crypto/crypto.dart';
class Cryptography {
/// Generates a cryptographically secure random nonce, to be included in a
/// credential request.
static String generateNonce([int length = 32]) {
const charset =
'0123456789ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvwxyz-._';
final random = Random.secure();
return List.generate(length, (_) => charset[random.nextInt(charset.length)])
.join();
}
/// Returns the sha256 hash of [input] in hex notation.
static String sha256ofString(String input) {
final bytes = utf8.encode(input);
final digest = sha256.convert(bytes);
return digest.toString();
}
}
@@ -20,11 +20,10 @@ import 'package:wyatt_type_utils/wyatt_type_utils.dart';
class AuthenticationCacheDataSourceImpl<T extends Object> class AuthenticationCacheDataSourceImpl<T extends Object>
extends AuthenticationCacheDataSource<T> { extends AuthenticationCacheDataSource<T> {
AuthenticationCacheDataSourceImpl();
Account? _account; Account? _account;
T? _data; T? _data;
AuthenticationCacheDataSourceImpl();
@override @override
Future<void> storeAccount(Account? account) async { Future<void> storeAccount(Account? account) async {
_account = account; _account = account;
@@ -14,34 +14,22 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // 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/data/models/account_model_firebase.dart';
import 'package:wyatt_authentication_bloc/wyatt_authentication_bloc.dart'; import 'package:wyatt_authentication_bloc/wyatt_authentication_bloc.dart';
import 'package:wyatt_type_utils/wyatt_type_utils.dart'; import 'package:wyatt_type_utils/wyatt_type_utils.dart';
class AuthenticationFirebaseDataSourceImpl class AuthenticationFirebaseDataSourceImpl
extends AuthenticationRemoteDataSource { extends AuthenticationRemoteDataSource {
AuthenticationFirebaseDataSourceImpl({
FirebaseAuth? firebaseAuth,
GoogleSignIn? googleSignIn,
}) : _firebaseAuth = firebaseAuth ?? FirebaseAuth.instance,
_googleSignIn = googleSignIn ?? GoogleSignIn();
final FirebaseAuth _firebaseAuth; final FirebaseAuth _firebaseAuth;
final GoogleSignIn _googleSignIn;
UserCredential? _latestCreds; UserCredential? _latestCreds;
AuthenticationFirebaseDataSourceImpl({FirebaseAuth? firebaseAuth})
: _firebaseAuth = firebaseAuth ?? FirebaseAuth.instance;
Account _mapper(User user) => AccountModel(
uid: user.uid,
emailVerified: user.emailVerified,
isAnonymous: user.isAnonymous,
providerId: user.providerData.first.providerId,
creationTime: user.metadata.creationTime,
lastSignInTime: user.metadata.lastSignInTime,
isNewUser: (user.metadata.creationTime != null &&
user.metadata.lastSignInTime != null)
? user.metadata.lastSignInTime! == user.metadata.creationTime!
: null,
email: user.email,
phoneNumber: user.phoneNumber,
photoURL: user.photoURL,
);
@override @override
Future<Account> signInWithEmailAndPassword({ Future<Account> signInWithEmailAndPassword({
required String email, required String email,
@@ -54,11 +42,7 @@ class AuthenticationFirebaseDataSourceImpl
); );
_latestCreds = userCredential; _latestCreds = userCredential;
final user = userCredential.user; final user = userCredential.user;
if (user.isNotNull) { return AccountModelFirebase.fromFirebaseUser(user);
return _mapper(user!);
} else {
throw Exception(); // Get caught just after.
}
} on FirebaseAuthException catch (e) { } on FirebaseAuthException catch (e) {
throw SignInWithEmailAndPasswordFailureFirebase.fromCode(e.code); throw SignInWithEmailAndPasswordFailureFirebase.fromCode(e.code);
} catch (_) { } catch (_) {
@@ -80,11 +64,7 @@ class AuthenticationFirebaseDataSourceImpl
); );
_latestCreds = userCredential; _latestCreds = userCredential;
final user = userCredential.user; final user = userCredential.user;
if (user.isNotNull) { return AccountModelFirebase.fromFirebaseUser(user);
return _mapper(user!);
} else {
throw Exception(); // Get caught just after.
}
} on FirebaseAuthException catch (e) { } on FirebaseAuthException catch (e) {
throw SignUpWithEmailAndPasswordFailureFirebase.fromCode(e.code); throw SignUpWithEmailAndPasswordFailureFirebase.fromCode(e.code);
} catch (_) { } catch (_) {
@@ -121,8 +101,11 @@ class AuthenticationFirebaseDataSourceImpl
@override @override
Stream<Account?> streamAccount() => Stream<Account?> streamAccount() =>
_firebaseAuth.userChanges().map<Account?>((user) { _firebaseAuth.userChanges().map<Account?>((user) {
final Account? account = (user.isNotNull) ? _mapper(user!) : null; try {
return account; return AccountModelFirebase.fromFirebaseUser(user);
} on FirebaseAuthException {
return null;
}
}); });
@override @override
@@ -170,11 +153,7 @@ class AuthenticationFirebaseDataSourceImpl
final userCredential = await _firebaseAuth.signInAnonymously(); final userCredential = await _firebaseAuth.signInAnonymously();
_latestCreds = userCredential; _latestCreds = userCredential;
final user = userCredential.user; final user = userCredential.user;
if (user.isNotNull) { return AccountModelFirebase.fromFirebaseUser(user);
return _mapper(user!);
} else {
throw Exception(); // Get caught just after.
}
} on FirebaseAuthException catch (e) { } on FirebaseAuthException catch (e) {
throw SignInAnonymouslyFailureFirebase.fromCode(e.code); throw SignInAnonymouslyFailureFirebase.fromCode(e.code);
} catch (_) { } catch (_) {
@@ -182,6 +161,35 @@ class AuthenticationFirebaseDataSourceImpl
} }
} }
@override
Future<Account> signInWithGoogle() async {
try {
// Trigger the authentication flow
final GoogleSignInAccount? googleUser = await _googleSignIn.signIn();
// Obtain the auth details from the request
final GoogleSignInAuthentication? googleAuth =
await googleUser?.authentication;
// Create a new credential
final credential = GoogleAuthProvider.credential(
accessToken: googleAuth?.accessToken,
idToken: googleAuth?.idToken,
);
final userCredential =
await _firebaseAuth.signInWithCredential(credential);
_latestCreds = userCredential;
final user = userCredential.user;
return AccountModelFirebase.fromFirebaseUser(user);
} on FirebaseAuthException catch (e) {
throw SignInWithGoogleFailureFirebase.fromCode(e.code);
} catch (_) {
throw SignInWithGoogleFailureFirebase();
}
}
@override @override
Future<bool> verifyPasswordResetCode({required String code}) async { Future<bool> verifyPasswordResetCode({required String code}) async {
try { try {
@@ -215,11 +223,7 @@ class AuthenticationFirebaseDataSourceImpl
throw Exception(); // Get caught just after. throw Exception(); // Get caught just after.
} }
final user = _firebaseAuth.currentUser; final user = _firebaseAuth.currentUser;
if (user.isNotNull) { return AccountModelFirebase.fromFirebaseUser(user);
return _mapper(user!);
} else {
throw Exception(); // Get caught just after.
}
} on FirebaseAuthException catch (e) { } on FirebaseAuthException catch (e) {
throw ReauthenticateFailureFirebase.fromCode(e.code); throw ReauthenticateFailureFirebase.fromCode(e.code);
} catch (_) { } catch (_) {
@@ -232,11 +236,7 @@ class AuthenticationFirebaseDataSourceImpl
try { try {
await _firebaseAuth.currentUser!.updateEmail(email); await _firebaseAuth.currentUser!.updateEmail(email);
final user = _firebaseAuth.currentUser; final user = _firebaseAuth.currentUser;
if (user.isNotNull) { return AccountModelFirebase.fromFirebaseUser(user);
return _mapper(user!);
} else {
throw Exception(); // Get caught just after.
}
} on FirebaseAuthException catch (e) { } on FirebaseAuthException catch (e) {
throw UpdateEmailFailureFirebase.fromCode(e.code); throw UpdateEmailFailureFirebase.fromCode(e.code);
} catch (_) { } catch (_) {
@@ -249,11 +249,7 @@ class AuthenticationFirebaseDataSourceImpl
try { try {
await _firebaseAuth.currentUser!.updatePassword(password); await _firebaseAuth.currentUser!.updatePassword(password);
final user = _firebaseAuth.currentUser; final user = _firebaseAuth.currentUser;
if (user.isNotNull) { return AccountModelFirebase.fromFirebaseUser(user);
return _mapper(user!);
} else {
throw Exception(); // Get caught just after.
}
} on FirebaseAuthException catch (e) { } on FirebaseAuthException catch (e) {
throw UpdatePasswordFailureFirebase.fromCode(e.code); throw UpdatePasswordFailureFirebase.fromCode(e.code);
} catch (_) { } catch (_) {
@@ -21,6 +21,10 @@ import 'package:wyatt_authentication_bloc/wyatt_authentication_bloc.dart';
import 'package:wyatt_type_utils/wyatt_type_utils.dart'; import 'package:wyatt_type_utils/wyatt_type_utils.dart';
class AuthenticationMockDataSourceImpl extends AuthenticationRemoteDataSource { class AuthenticationMockDataSourceImpl extends AuthenticationRemoteDataSource {
AuthenticationMockDataSourceImpl({
this.idToken = 'fake-id-token',
this.registeredAccounts,
});
Pair<Account, String>? _connectedMock; Pair<Account, String>? _connectedMock;
Pair<Account, String>? _registeredMock; Pair<Account, String>? _registeredMock;
DateTime _lastSignInTime = DateTime.now(); DateTime _lastSignInTime = DateTime.now();
@@ -30,11 +34,6 @@ class AuthenticationMockDataSourceImpl extends AuthenticationRemoteDataSource {
final List<Pair<Account, String>>? registeredAccounts; final List<Pair<Account, String>>? registeredAccounts;
final String idToken; final String idToken;
AuthenticationMockDataSourceImpl({
this.idToken = 'fake-id-token',
this.registeredAccounts,
});
Future<void> _randomDelay() async { Future<void> _randomDelay() async {
await Future<void>.delayed( await Future<void>.delayed(
Duration(milliseconds: Random().nextInt(400) + 200), Duration(milliseconds: Random().nextInt(400) + 200),
@@ -112,7 +111,26 @@ class AuthenticationMockDataSourceImpl extends AuthenticationRemoteDataSource {
uid: 'mock-id-anom', uid: 'mock-id-anom',
emailVerified: false, emailVerified: false,
isAnonymous: true, isAnonymous: true,
providerId: 'wyatt', providerId: 'wyatt-studio.fr',
creationTime: creation,
lastSignInTime: creation,
isNewUser: creation == creation,
);
_streamAccount.add(mock);
_connectedMock = _connectedMock?.copyWith(left: mock);
_lastSignInTime = DateTime.now();
return Future.value(mock);
}
@override
Future<Account> signInWithGoogle() async {
await _randomDelay();
final creation = DateTime.now();
final mock = AccountModel(
uid: 'mock-id-google',
emailVerified: true,
isAnonymous: false,
providerId: 'google.com',
creationTime: creation, creationTime: creation,
lastSignInTime: creation, lastSignInTime: creation,
isNewUser: creation == creation, isNewUser: creation == creation,
@@ -72,7 +72,8 @@ class AccountModel extends Account {
String? phoneNumber, String? phoneNumber,
String? photoURL, String? photoURL,
String? providerId, String? providerId,
}) => AccountModel( }) =>
AccountModel(
uid: uid ?? this.uid, uid: uid ?? this.uid,
email: email ?? this.email, email: email ?? this.email,
creationTime: creationTime ?? this.creationTime, creationTime: creationTime ?? this.creationTime,
@@ -0,0 +1,55 @@
// ignore_for_file: public_member_api_docs, sort_constructors_first
// 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/wyatt_authentication_bloc.dart';
class AccountModelFirebase extends AccountModel {
AccountModelFirebase._({
required super.uid,
required super.emailVerified,
required super.isAnonymous,
required super.providerId,
super.lastSignInTime,
super.creationTime,
super.isNewUser,
super.email,
super.phoneNumber,
super.photoURL,
});
factory AccountModelFirebase.fromFirebaseUser(User? user) {
if (user != null) {
return AccountModelFirebase._(
uid: user.uid,
emailVerified: user.emailVerified,
isAnonymous: user.isAnonymous,
providerId: user.providerData.first.providerId,
creationTime: user.metadata.creationTime,
lastSignInTime: user.metadata.lastSignInTime,
isNewUser: (user.metadata.creationTime != null &&
user.metadata.lastSignInTime != null)
? user.metadata.lastSignInTime! == user.metadata.creationTime!
: null,
email: user.email,
phoneNumber: user.phoneNumber,
photoURL: user.photoURL,
);
} else {
throw ModelParsingFailureFirebase('null-user', 'User cannot be null!');
}
}
}
@@ -29,7 +29,8 @@ class AccountWrapperModel<T> extends AccountWrapper<T> {
AccountWrapperModel<T> copyWith({ AccountWrapperModel<T> copyWith({
Account? account, Account? account,
T? data, T? data,
}) => AccountWrapperModel<T>( }) =>
AccountWrapperModel<T>(
account ?? this.account, account ?? this.account,
data ?? this.data, data ?? this.data,
); );
@@ -42,19 +42,6 @@ typedef OnAuthChange<T> = FutureOrResult<T?> Function(
class AuthenticationRepositoryImpl<T extends Object> class AuthenticationRepositoryImpl<T extends Object>
extends AuthenticationRepository<T> { extends AuthenticationRepository<T> {
final AuthenticationCacheDataSource<T> _authenticationLocalDataSource;
final AuthenticationRemoteDataSource _authenticationRemoteDataSource;
late FormRepository _formRepository;
final OnSignUpSuccess<T>? _onSignUpSuccess;
final OnAuthChange<T>? _onAccountChanges;
final StreamController<FutureOrResult<AccountWrapper<T>>> _signUpStream =
StreamController();
bool _pause = false; // Semaphore
AuthenticationRepositoryImpl({ AuthenticationRepositoryImpl({
required AuthenticationCacheDataSource<T> authenticationCacheDataSource, required AuthenticationCacheDataSource<T> authenticationCacheDataSource,
required AuthenticationRemoteDataSource authenticationRemoteDataSource, required AuthenticationRemoteDataSource authenticationRemoteDataSource,
@@ -106,6 +93,18 @@ class AuthenticationRepositoryImpl<T extends Object>
), ),
); );
} }
final AuthenticationCacheDataSource<T> _authenticationLocalDataSource;
final AuthenticationRemoteDataSource _authenticationRemoteDataSource;
late FormRepository _formRepository;
final OnSignUpSuccess<T>? _onSignUpSuccess;
final OnAuthChange<T>? _onAccountChanges;
final StreamController<FutureOrResult<AccountWrapper<T>>> _signUpStream =
StreamController();
bool _pause = false;
@override @override
FormRepository get formRepository => _formRepository; FormRepository get formRepository => _formRepository;
@@ -295,6 +294,17 @@ class AuthenticationRepositoryImpl<T extends Object>
(error) => error, (error) => error,
); );
@override
FutureOrResult<Account> signInWithGoogle() =>
Result.tryCatchAsync<Account, AppException, AppException>(
() async {
final account =
await _authenticationRemoteDataSource.signInWithGoogle();
return account;
},
(error) => error,
);
@override @override
FutureOrResult<bool> verifyPasswordResetCode({required String code}) => FutureOrResult<bool> verifyPasswordResetCode({required String code}) =>
Result.tryCatchAsync<bool, AppException, AppException>( Result.tryCatchAsync<bool, AppException, AppException>(
@@ -49,6 +49,8 @@ abstract class AuthenticationRemoteDataSource extends BaseRemoteDataSource {
Future<Account> signInAnonymously(); Future<Account> signInAnonymously();
Future<Account> signInWithGoogle();
Future<Account> updateEmail({required String email}); Future<Account> updateEmail({required String email});
Future<Account> updatePassword({required String password}); Future<Account> updatePassword({required String password});
@@ -73,6 +73,13 @@ abstract class AuthenticationRepository<T> extends BaseRepository {
/// {@endtemplate} /// {@endtemplate}
FutureOrResult<Account> signInAnonymously(); FutureOrResult<Account> signInAnonymously();
/// {@template signin_google}
/// Starts the Sign In with Google Flow.
///
/// Throws a SignInWithGoogleFailureInterface if an exception occurs.
/// {@endtemplate}
FutureOrResult<Account> signInWithGoogle();
/// {@template signin_pwd} /// {@template signin_pwd}
/// Signs in with the provided [email] and [password]. /// Signs in with the provided [email] and [password].
/// ///
@@ -26,14 +26,13 @@ import 'package:wyatt_type_utils/wyatt_type_utils.dart';
part 'authentication_state.dart'; part 'authentication_state.dart';
class AuthenticationCubit<Extra> extends Cubit<AuthenticationState<Extra>> { class AuthenticationCubit<Extra> extends Cubit<AuthenticationState<Extra>> {
final AuthenticationRepository<Extra> _authenticationRepository;
AuthenticationCubit({ AuthenticationCubit({
required AuthenticationRepository<Extra> authenticationRepository, required AuthenticationRepository<Extra> authenticationRepository,
}) : _authenticationRepository = authenticationRepository, }) : _authenticationRepository = authenticationRepository,
super(const AuthenticationState.unknown()) { super(const AuthenticationState.unknown()) {
_listenForAuthenticationChanges(); _listenForAuthenticationChanges();
} }
final AuthenticationRepository<Extra> _authenticationRepository;
void _listenForAuthenticationChanges() { void _listenForAuthenticationChanges() {
_authenticationRepository.streamAccount().listen((accountFutureResult) { _authenticationRepository.streamAccount().listen((accountFutureResult) {
@@ -17,13 +17,8 @@
part of 'authentication_cubit.dart'; part of 'authentication_cubit.dart';
class AuthenticationState<Extra> extends Equatable { class AuthenticationState<Extra> extends Equatable {
final AuthenticationStatus status; const AuthenticationState.unauthenticated()
final AccountWrapper<Extra>? accountWrapper; : this._(status: AuthenticationStatus.unauthenticated);
const AuthenticationState._({required this.status, this.accountWrapper});
const AuthenticationState.unknown()
: this._(status: AuthenticationStatus.unknown);
const AuthenticationState.authenticated(AccountWrapper<Extra> accountWrapper) const AuthenticationState.authenticated(AccountWrapper<Extra> accountWrapper)
: this._( : this._(
@@ -31,8 +26,12 @@ class AuthenticationState<Extra> extends Equatable {
accountWrapper: accountWrapper, accountWrapper: accountWrapper,
); );
const AuthenticationState.unauthenticated() const AuthenticationState.unknown()
: this._(status: AuthenticationStatus.unauthenticated); : this._(status: AuthenticationStatus.unknown);
const AuthenticationState._({required this.status, this.accountWrapper});
final AuthenticationStatus status;
final AccountWrapper<Extra>? accountWrapper;
@override @override
List<Object?> get props => [status, accountWrapper]; List<Object?> get props => [status, accountWrapper];
@@ -24,12 +24,11 @@ import 'package:wyatt_form_bloc/wyatt_form_bloc.dart';
part 'email_verification_state.dart'; part 'email_verification_state.dart';
class EmailVerificationCubit<Extra> extends Cubit<EmailVerificationState> { class EmailVerificationCubit<Extra> extends Cubit<EmailVerificationState> {
final AuthenticationRepository<Extra> _authenticationRepository;
EmailVerificationCubit({ EmailVerificationCubit({
required AuthenticationRepository<Extra> authenticationRepository, required AuthenticationRepository<Extra> authenticationRepository,
}) : _authenticationRepository = authenticationRepository, }) : _authenticationRepository = authenticationRepository,
super(const EmailVerificationState()); super(const EmailVerificationState());
final AuthenticationRepository<Extra> _authenticationRepository;
FutureOr<void> sendEmailVerification() async { FutureOr<void> sendEmailVerification() async {
emit(state.copyWith(status: FormStatus.submissionInProgress)); emit(state.copyWith(status: FormStatus.submissionInProgress));
@@ -25,10 +25,6 @@ import 'package:wyatt_type_utils/wyatt_type_utils.dart';
part 'password_reset_state.dart'; part 'password_reset_state.dart';
class PasswordResetCubit<Extra> extends FormDataCubit<PasswordResetState> { class PasswordResetCubit<Extra> extends FormDataCubit<PasswordResetState> {
final AuthenticationRepository<Extra> _authenticationRepository;
FormRepository get _formRepository =>
_authenticationRepository.formRepository;
PasswordResetCubit({ PasswordResetCubit({
required AuthenticationRepository<Extra> authenticationRepository, required AuthenticationRepository<Extra> authenticationRepository,
}) : _authenticationRepository = authenticationRepository, }) : _authenticationRepository = authenticationRepository,
@@ -38,6 +34,9 @@ class PasswordResetCubit<Extra> extends FormDataCubit<PasswordResetState> {
.accessForm(AuthFormName.passwordResetForm), .accessForm(AuthFormName.passwordResetForm),
), ),
); );
final AuthenticationRepository<Extra> _authenticationRepository;
FormRepository get _formRepository =>
_authenticationRepository.formRepository;
@override @override
String get formName => AuthFormName.passwordResetForm; String get formName => AuthFormName.passwordResetForm;
@@ -17,13 +17,12 @@
part of 'password_reset_cubit.dart'; part of 'password_reset_cubit.dart';
class PasswordResetState extends FormDataState { class PasswordResetState extends FormDataState {
Email get email => form.validatorOf(AuthFormField.email);
const PasswordResetState({ const PasswordResetState({
required super.form, required super.form,
super.status = FormStatus.pure, super.status = FormStatus.pure,
super.errorMessage, super.errorMessage,
}); });
Email get email => form.validatorOf(AuthFormField.email);
PasswordResetState copyWith({ PasswordResetState copyWith({
WyattForm? form, WyattForm? form,
@@ -23,10 +23,6 @@ import 'package:wyatt_type_utils/wyatt_type_utils.dart';
part 'sign_in_state.dart'; part 'sign_in_state.dart';
class SignInCubit<Extra> extends FormDataCubit<SignInState> { class SignInCubit<Extra> extends FormDataCubit<SignInState> {
final AuthenticationRepository<Extra> _authenticationRepository;
FormRepository get _formRepository =>
_authenticationRepository.formRepository;
SignInCubit({ SignInCubit({
required AuthenticationRepository<Extra> authenticationRepository, required AuthenticationRepository<Extra> authenticationRepository,
}) : _authenticationRepository = authenticationRepository, }) : _authenticationRepository = authenticationRepository,
@@ -36,6 +32,9 @@ class SignInCubit<Extra> extends FormDataCubit<SignInState> {
.accessForm(AuthFormName.signInForm), .accessForm(AuthFormName.signInForm),
), ),
); );
final AuthenticationRepository<Extra> _authenticationRepository;
FormRepository get _formRepository =>
_authenticationRepository.formRepository;
@override @override
String get formName => AuthFormName.signInForm; String get formName => AuthFormName.signInForm;
@@ -206,4 +205,24 @@ class SignInCubit<Extra> extends FormDataCubit<SignInState> {
), ),
); );
} }
FutureOr<void> signInWithGoogle() async {
if (state.status.isSubmissionInProgress) {
return;
}
// TODO(wyatt): maybe emit new state (to not carry an old errorMessage)
emit(state.copyWith(status: FormStatus.submissionInProgress));
final uid = await _authenticationRepository.signInWithGoogle();
emit(
uid.fold(
(value) => state.copyWith(status: FormStatus.submissionSuccess),
(error) => state.copyWith(
errorMessage: error.message,
status: FormStatus.submissionFailure,
),
),
);
}
} }
@@ -17,16 +17,15 @@
part of 'sign_in_cubit.dart'; part of 'sign_in_cubit.dart';
class SignInState extends FormDataState { class SignInState extends FormDataState {
FormInputValidator<String?, ValidationError> get email =>
form.validatorOf(AuthFormField.email);
FormInputValidator<String?, ValidationError> get password =>
form.validatorOf(AuthFormField.password);
const SignInState({ const SignInState({
required super.form, required super.form,
super.status = FormStatus.pure, super.status = FormStatus.pure,
super.errorMessage, super.errorMessage,
}); });
FormInputValidator<String?, ValidationError> get email =>
form.validatorOf(AuthFormField.email);
FormInputValidator<String?, ValidationError> get password =>
form.validatorOf(AuthFormField.password);
SignInState copyWith({ SignInState copyWith({
WyattForm? form, WyattForm? form,
@@ -25,10 +25,6 @@ import 'package:wyatt_type_utils/wyatt_type_utils.dart';
part 'sign_up_state.dart'; part 'sign_up_state.dart';
class SignUpCubit<Extra> extends FormDataCubit<SignUpState> { class SignUpCubit<Extra> extends FormDataCubit<SignUpState> {
final AuthenticationRepository<Extra> _authenticationRepository;
FormRepository get _formRepository =>
_authenticationRepository.formRepository;
SignUpCubit({ SignUpCubit({
required AuthenticationRepository<Extra> authenticationRepository, required AuthenticationRepository<Extra> authenticationRepository,
}) : _authenticationRepository = authenticationRepository, }) : _authenticationRepository = authenticationRepository,
@@ -38,6 +34,9 @@ class SignUpCubit<Extra> extends FormDataCubit<SignUpState> {
.accessForm(AuthFormName.signUpForm), .accessForm(AuthFormName.signUpForm),
), ),
); );
final AuthenticationRepository<Extra> _authenticationRepository;
FormRepository get _formRepository =>
_authenticationRepository.formRepository;
@override @override
String get formName => AuthFormName.signUpForm; String get formName => AuthFormName.signUpForm;
@@ -17,16 +17,15 @@
part of 'sign_up_cubit.dart'; part of 'sign_up_cubit.dart';
class SignUpState extends FormDataState { class SignUpState extends FormDataState {
FormInputValidator<String?, ValidationError> get email =>
form.validatorOf(AuthFormField.email);
FormInputValidator<String?, ValidationError> get password =>
form.validatorOf(AuthFormField.password);
const SignUpState({ const SignUpState({
required super.form, required super.form,
super.status = FormStatus.pure, super.status = FormStatus.pure,
super.errorMessage, super.errorMessage,
}); });
FormInputValidator<String?, ValidationError> get email =>
form.validatorOf(AuthFormField.email);
FormInputValidator<String?, ValidationError> get password =>
form.validatorOf(AuthFormField.password);
SignUpState copyWith({ SignUpState copyWith({
WyattForm? form, WyattForm? form,
@@ -17,4 +17,7 @@
/// An authentication library for BLoC. /// An authentication library for BLoC.
library wyatt_authentication_bloc; library wyatt_authentication_bloc;
export 'package:firebase_auth/firebase_auth.dart';
export 'package:google_sign_in/google_sign_in.dart';
export 'src/src.dart'; export 'src/src.dart';
@@ -1,7 +1,7 @@
name: wyatt_authentication_bloc name: wyatt_authentication_bloc
description: Authentication BLoC for Flutter description: Authentication BLoC for Flutter
repository: https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages/src/branch/master/packages/wyatt_authentication_bloc repository: https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages/src/branch/master/packages/wyatt_authentication_bloc
version: 0.4.0+3 version: 0.4.1
publish_to: https://git.wyatt-studio.fr/api/packages/Wyatt-FOSS/pub publish_to: https://git.wyatt-studio.fr/api/packages/Wyatt-FOSS/pub
@@ -10,17 +10,12 @@ environment:
flutter: ">=1.17.0" flutter: ">=1.17.0"
dependencies: dependencies:
flutter: flutter: { sdk: flutter }
sdk: flutter
crypto: ^3.0.2 crypto: ^3.0.2
flutter_bloc: ^8.1.1 flutter_bloc: ^8.1.1
equatable: ^2.0.5 equatable: ^2.0.5
firebase_auth: ^4.1.1 firebase_auth: ^4.2.0
google_sign_in: ^5.3.0 google_sign_in: ^5.4.2
flutter_facebook_auth: ^4.3.0
sign_in_with_apple: ^3.3.0
twitter_login: ^4.2.3
rxdart: ^0.27.7 rxdart: ^0.27.7
wyatt_form_bloc: wyatt_form_bloc:
@@ -36,8 +31,7 @@ dependencies:
version: ^0.0.4 version: ^0.0.4
dev_dependencies: dev_dependencies:
flutter_test: flutter_test: { sdk: flutter }
sdk: flutter
bloc_test: ^9.1.0 bloc_test: ^9.1.0
mocktail: ^0.3.0 mocktail: ^0.3.0
@@ -24,14 +24,14 @@ import 'package:wyatt_crud_bloc/src/domain/entities/query.dart';
class CrudInMemoryDataSourceImpl<Model extends ObjectModel> class CrudInMemoryDataSourceImpl<Model extends ObjectModel>
extends CrudDataSource<Model> { extends CrudDataSource<Model> {
CrudInMemoryDataSourceImpl({required this.toMap, Map<String, Model>? data})
: _data = data ?? {};
final Map<String, Model> _data; final Map<String, Model> _data;
final StreamController<List<Model?>> _streamData = StreamController(); final StreamController<List<Model?>> _streamData = StreamController();
final Map<String, Object?> Function(Model) toMap; final Map<String, Object?> Function(Model) toMap;
CrudInMemoryDataSourceImpl({required this.toMap, Map<String, Model>? data})
: _data = data ?? {};
@override @override
Future<void> create(Model object, {String? id}) async { Future<void> create(Model object, {String? id}) async {
_data[id ?? object.id ?? ''] = object; _data[id ?? object.id ?? ''] = object;
@@ -22,10 +22,6 @@ import 'package:wyatt_crud_bloc/src/domain/entities/query.dart';
class CrudFirestoreDataSourceImpl<Model extends ObjectModel, Entity> class CrudFirestoreDataSourceImpl<Model extends ObjectModel, Entity>
extends CrudDataSource<Model> { extends CrudDataSource<Model> {
final FirebaseFirestore _firestore;
final Map<String, Object?> Function(Model, SetOptions?) _toFirestore;
late CollectionReference<Model> _collectionReference;
CrudFirestoreDataSourceImpl( CrudFirestoreDataSourceImpl(
String collection, { String collection, {
@@ -44,6 +40,10 @@ class CrudFirestoreDataSourceImpl<Model extends ObjectModel, Entity>
toFirestore: toFirestore, toFirestore: toFirestore,
); );
} }
final FirebaseFirestore _firestore;
final Map<String, Object?> Function(Model, SetOptions?) _toFirestore;
late CollectionReference<Model> _collectionReference;
@override @override
Future<void> create(Model object, {String? id}) { Future<void> create(Model object, {String? id}) {
@@ -23,11 +23,11 @@ import 'package:wyatt_type_utils/wyatt_type_utils.dart';
class CrudRepositoryImpl<Model extends ObjectModel> class CrudRepositoryImpl<Model extends ObjectModel>
extends CrudRepository<Model> { extends CrudRepository<Model> {
final CrudDataSource<Model> _crudDataSource;
CrudRepositoryImpl({ CrudRepositoryImpl({
required CrudDataSource<Model> crudDataSource, required CrudDataSource<Model> crudDataSource,
}) : _crudDataSource = crudDataSource; }) : _crudDataSource = crudDataSource;
final CrudDataSource<Model> _crudDataSource;
@override @override
FutureOrResult<void> create(Model object, {String? id}) => FutureOrResult<void> create(Model object, {String? id}) =>
@@ -25,22 +25,22 @@ abstract class QueryParser<Q> {
abstract class QueryInterface extends Entity {} abstract class QueryInterface extends Entity {}
class WhereQuery<Value> extends QueryInterface { class WhereQuery<Value> extends QueryInterface {
WhereQuery(this.type, this.field, this.value);
final WhereQueryType type; final WhereQueryType type;
final String field; final String field;
final Value value; final Value value;
WhereQuery(this.type, this.field, this.value);
} }
class LimitQuery extends QueryInterface { class LimitQuery extends QueryInterface {
final int limit;
LimitQuery(this.limit); LimitQuery(this.limit);
final int limit;
} }
class OrderByQuery extends QueryInterface { class OrderByQuery extends QueryInterface {
final String field;
final bool ascending;
OrderByQuery(this.field, {this.ascending = true}); OrderByQuery(this.field, {this.ascending = true});
final String field;
final bool ascending;
} }
@@ -21,9 +21,9 @@ import 'package:wyatt_crud_bloc/src/domain/entities/object_model.dart';
import 'package:wyatt_crud_bloc/src/domain/repositories/crud_repository.dart'; import 'package:wyatt_crud_bloc/src/domain/repositories/crud_repository.dart';
class Delete<Model extends ObjectModel> extends AsyncUseCase<String, void> { class Delete<Model extends ObjectModel> extends AsyncUseCase<String, void> {
final CrudRepository<Model> _crudRepository;
Delete(this._crudRepository); Delete(this._crudRepository);
final CrudRepository<Model> _crudRepository;
@override @override
FutureOr<void> onStart(String? params) { FutureOr<void> onStart(String? params) {
@@ -19,9 +19,9 @@ import 'package:wyatt_crud_bloc/src/domain/entities/object_model.dart';
import 'package:wyatt_crud_bloc/src/domain/repositories/crud_repository.dart'; import 'package:wyatt_crud_bloc/src/domain/repositories/crud_repository.dart';
class DeleteAll<Model extends ObjectModel> extends AsyncUseCase<void, void> { class DeleteAll<Model extends ObjectModel> extends AsyncUseCase<void, void> {
final CrudRepository<Model> _crudRepository;
DeleteAll(this._crudRepository); DeleteAll(this._crudRepository);
final CrudRepository<Model> _crudRepository;
@override @override
FutureOrResult<void> execute(void params) => _crudRepository.deleteAll(); FutureOrResult<void> execute(void params) => _crudRepository.deleteAll();
@@ -21,9 +21,9 @@ import 'package:wyatt_crud_bloc/src/domain/entities/object_model.dart';
import 'package:wyatt_crud_bloc/src/domain/repositories/crud_repository.dart'; import 'package:wyatt_crud_bloc/src/domain/repositories/crud_repository.dart';
class Get<Model extends ObjectModel> extends AsyncUseCase<String, Model?> { class Get<Model extends ObjectModel> extends AsyncUseCase<String, Model?> {
final CrudRepository<Model> _crudRepository;
Get(this._crudRepository); Get(this._crudRepository);
final CrudRepository<Model> _crudRepository;
@override @override
FutureOr<void> onStart(String? params) { FutureOr<void> onStart(String? params) {
@@ -20,9 +20,9 @@ import 'package:wyatt_crud_bloc/src/domain/repositories/crud_repository.dart';
class GetAll<Model extends ObjectModel> class GetAll<Model extends ObjectModel>
extends AsyncUseCase<void, List<Model?>> { extends AsyncUseCase<void, List<Model?>> {
final CrudRepository<Model> _crudRepository;
GetAll(this._crudRepository); GetAll(this._crudRepository);
final CrudRepository<Model> _crudRepository;
@override @override
FutureOrResult<List<Model?>> execute(void params) => _crudRepository.getAll(); FutureOrResult<List<Model?>> execute(void params) => _crudRepository.getAll();
@@ -23,9 +23,9 @@ import 'package:wyatt_crud_bloc/src/domain/repositories/crud_repository.dart';
class Query<Model extends ObjectModel> class Query<Model extends ObjectModel>
extends AsyncUseCase<List<QueryInterface>, List<Model?>> { extends AsyncUseCase<List<QueryInterface>, List<Model?>> {
final CrudRepository<Model> _crudRepository;
Query(this._crudRepository); Query(this._crudRepository);
final CrudRepository<Model> _crudRepository;
@override @override
FutureOr<void> onStart(List<QueryInterface>? params) { FutureOr<void> onStart(List<QueryInterface>? params) {
@@ -23,9 +23,9 @@ import 'package:wyatt_crud_bloc/src/domain/usecases/params/update_parameters.dar
class Update<Model extends ObjectModel> class Update<Model extends ObjectModel>
extends AsyncUseCase<UpdateParameters<Model>, void> { extends AsyncUseCase<UpdateParameters<Model>, void> {
final CrudRepository<Model> _crudRepository;
Update(this._crudRepository); Update(this._crudRepository);
final CrudRepository<Model> _crudRepository;
@override @override
FutureOr<void> onStart(UpdateParameters<Model>? params) { FutureOr<void> onStart(UpdateParameters<Model>? params) {
@@ -22,9 +22,9 @@ import 'package:wyatt_crud_bloc/src/domain/repositories/crud_repository.dart';
class UpdateAll<Model extends ObjectModel> class UpdateAll<Model extends ObjectModel>
extends AsyncUseCase<Map<String, Object?>, void> { extends AsyncUseCase<Map<String, Object?>, void> {
final CrudRepository<Model> _crudRepository;
UpdateAll(this._crudRepository); UpdateAll(this._crudRepository);
final CrudRepository<Model> _crudRepository;
@override @override
FutureOr<void> onStart(Map<String, Object?>? params) { FutureOr<void> onStart(Map<String, Object?>? params) {
@@ -35,6 +35,8 @@ import 'package:wyatt_crud_bloc/src/domain/usecases/update_all.dart';
part 'crud_state.dart'; part 'crud_state.dart';
abstract class CrudCubit<Model extends ObjectModel> extends Cubit<CrudState> { abstract class CrudCubit<Model extends ObjectModel> extends Cubit<CrudState> {
CrudCubit() : super(CrudInitial());
Create<Model>? get crudCreate; Create<Model>? get crudCreate;
DeleteAll<Model>? get crudDeleteAll; DeleteAll<Model>? get crudDeleteAll;
Delete<Model>? get crudDelete; Delete<Model>? get crudDelete;
@@ -44,8 +46,6 @@ abstract class CrudCubit<Model extends ObjectModel> extends Cubit<CrudState> {
UpdateAll<Model>? get crudUpdateAll; UpdateAll<Model>? get crudUpdateAll;
Update<Model>? get crudUpdate; Update<Model>? get crudUpdate;
CrudCubit() : super(CrudInitial());
FutureOr<void> create(Model model) async { FutureOr<void> create(Model model) async {
if (crudCreate != null) { if (crudCreate != null) {
final stateCopy = state; final stateCopy = state;
@@ -36,27 +36,27 @@ class CrudOkReturn extends CrudState {
} }
class CrudError extends CrudState { class CrudError extends CrudState {
final String? message;
const CrudError(this.message); const CrudError(this.message);
final String? message;
@override @override
List<Object?> get props => [message]; List<Object?> get props => [message];
} }
class CrudLoaded<T> extends CrudSuccess { class CrudLoaded<T> extends CrudSuccess {
final T? data;
const CrudLoaded(this.data); const CrudLoaded(this.data);
final T? data;
@override @override
List<Object?> get props => [data]; List<Object?> get props => [data];
} }
class CrudListLoaded<T> extends CrudSuccess { class CrudListLoaded<T> extends CrudSuccess {
final List<T?> data;
const CrudListLoaded(this.data); const CrudListLoaded(this.data);
final List<T?> data;
@override @override
List<Object> get props => [data]; List<Object> get props => [data];
@@ -26,15 +26,6 @@ import 'package:wyatt_form_bloc/src/domain/input_validators/form_input_validator
// ignore: must_be_immutable // ignore: must_be_immutable
class WyattFormImpl extends WyattForm { class WyattFormImpl extends WyattForm {
final List<
FormInput<dynamic, FormInputValidator<dynamic, ValidationError>,
dynamic>> _inputs;
final FormValidator _validator;
final String _name;
late List<
FormInput<dynamic, FormInputValidator<dynamic, ValidationError>,
dynamic>> _inputsInitial;
WyattFormImpl( WyattFormImpl(
this._inputs, { this._inputs, {
@@ -44,6 +35,15 @@ class WyattFormImpl extends WyattForm {
_validator = validationStrategy { _validator = validationStrategy {
_inputsInitial = _inputs.map((input) => input.clone()).toList(); _inputsInitial = _inputs.map((input) => input.clone()).toList();
} }
final List<
FormInput<dynamic, FormInputValidator<dynamic, ValidationError>,
dynamic>> _inputs;
final FormValidator _validator;
final String _name;
late List<
FormInput<dynamic, FormInputValidator<dynamic, ValidationError>,
dynamic>> _inputsInitial;
@override @override
List< List<
@@ -17,6 +17,12 @@
part of 'form_data_cubit.dart'; part of 'form_data_cubit.dart';
abstract class FormDataState extends Equatable { abstract class FormDataState extends Equatable {
const FormDataState({
required this.form,
this.status = FormStatus.pure,
this.errorMessage,
});
/// Global status of a form. /// Global status of a form.
final FormStatus status; final FormStatus status;
@@ -26,12 +32,6 @@ abstract class FormDataState extends Equatable {
/// Optional error message. /// Optional error message.
final String? errorMessage; final String? errorMessage;
const FormDataState({
required this.form,
this.status = FormStatus.pure,
this.errorMessage,
});
@override @override
List<Object?> get props => [status, form, errorMessage]; List<Object?> get props => [status, form, errorMessage];
} }
@@ -27,11 +27,11 @@ import 'package:wyatt_form_bloc/src/presentation/features/form_data/form_data_cu
part 'form_data_state_impl.dart'; part 'form_data_state_impl.dart';
abstract class FormDataCubitImpl extends FormDataCubit<FormDataStateImpl> { abstract class FormDataCubitImpl extends FormDataCubit<FormDataStateImpl> {
final FormRepository _formRepository;
final String _formName;
FormDataCubitImpl(this._formRepository, this._formName) FormDataCubitImpl(this._formRepository, this._formName)
: super(FormDataStateImpl(form: _formRepository.accessForm(_formName))); : super(FormDataStateImpl(form: _formRepository.accessForm(_formName)));
final FormRepository _formRepository;
final String _formName;
@override @override
String get formName => _formName; String get formName => _formName;
@@ -115,7 +115,7 @@ Future<void> server() async {
final server = await HttpServer.bind(InternetAddress.anyIPv6, 8080); final server = await HttpServer.bind(InternetAddress.anyIPv6, 8080);
var error = 0; var error = 0;
var token = 0; var token = 0;
await server.forEach((HttpRequest request) { await server.forEach((request) {
print('[${request.method}] ${request.uri}'); print('[${request.method}] ${request.uri}');
switch (request.uri.path) { switch (request.uri.path) {
case '/test/basic-test': case '/test/basic-test':
@@ -196,7 +196,7 @@ Future<void> server() async {
Future<void> main() async { Future<void> main() async {
unawaited(server()); unawaited(server());
final base = 'localhost:8080'; const base = 'localhost:8080';
final uriPrefix = UriPrefixMiddleware( final uriPrefix = UriPrefixMiddleware(
protocol: Protocols.http, protocol: Protocols.http,
authority: base, authority: base,
@@ -31,19 +31,15 @@ enum EmailVerificationAction {
resetPassword, resetPassword,
changeEmail; changeEmail;
String toSnakeCase() { String toSnakeCase() => name.splitMapJoin(
return name.splitMapJoin(
RegExp('[A-Z]'), RegExp('[A-Z]'),
onMatch: (m) => '_${m[0]?.toLowerCase()}', onMatch: (m) => '_${m[0]?.toLowerCase()}',
onNonMatch: (n) => n, onNonMatch: (n) => n,
); );
}
factory EmailVerificationAction.fromString(String str) { factory EmailVerificationAction.fromString(String str) => EmailVerificationAction.values.firstWhere(
return EmailVerificationAction.values.firstWhere( (element) => element.toSnakeCase() == str,
(EmailVerificationAction element) => element.toSnakeCase() == str,
); );
}
} }
class VerifyCode { class VerifyCode {
@@ -60,29 +56,23 @@ class VerifyCode {
String? email, String? email,
String? verificationCode, String? verificationCode,
EmailVerificationAction? action, EmailVerificationAction? action,
}) { }) => VerifyCode(
return VerifyCode(
email: email ?? this.email, email: email ?? this.email,
verificationCode: verificationCode ?? this.verificationCode, verificationCode: verificationCode ?? this.verificationCode,
action: action ?? this.action, action: action ?? this.action,
); );
}
Map<String, dynamic> toMap() { Map<String, dynamic> toMap() => <String, dynamic>{
return <String, dynamic>{
'email': email, 'email': email,
'verification_code': verificationCode, 'verification_code': verificationCode,
'action': action.toSnakeCase(), 'action': action.toSnakeCase(),
}; };
}
factory VerifyCode.fromMap(Map<String, dynamic> map) { factory VerifyCode.fromMap(Map<String, dynamic> map) => VerifyCode(
return VerifyCode(
email: map['email'] as String, email: map['email'] as String,
verificationCode: map['verification_code'] as String, verificationCode: map['verification_code'] as String,
action: EmailVerificationAction.fromString(map['action'] as String), action: EmailVerificationAction.fromString(map['action'] as String),
); );
}
String toJson() => json.encode(toMap()); String toJson() => json.encode(toMap());
@@ -105,26 +95,20 @@ class Account {
Account copyWith({ Account copyWith({
String? email, String? email,
String? sessionId, String? sessionId,
}) { }) => Account(
return Account(
email: email ?? this.email, email: email ?? this.email,
sessionId: sessionId ?? this.sessionId, sessionId: sessionId ?? this.sessionId,
); );
}
Map<String, dynamic> toMap() { Map<String, dynamic> toMap() => <String, dynamic>{
return <String, dynamic>{
'email': email, 'email': email,
'session_id': sessionId, 'session_id': sessionId,
}; };
}
factory Account.fromMap(Map<String, dynamic> map) { factory Account.fromMap(Map<String, dynamic> map) => Account(
return Account(
email: map['email'] as String, email: map['email'] as String,
sessionId: map['session_id'] != null ? map['session_id'] as String : null, sessionId: map['session_id'] != null ? map['session_id'] as String : null,
); );
}
String toJson() => json.encode(toMap()); String toJson() => json.encode(toMap());
@@ -146,26 +130,20 @@ class SignUp {
SignUp copyWith({ SignUp copyWith({
String? sessionId, String? sessionId,
String? password, String? password,
}) { }) => SignUp(
return SignUp(
sessionId: sessionId ?? this.sessionId, sessionId: sessionId ?? this.sessionId,
password: password ?? this.password, password: password ?? this.password,
); );
}
Map<String, dynamic> toMap() { Map<String, dynamic> toMap() => <String, dynamic>{
return <String, dynamic>{
'session_id': sessionId, 'session_id': sessionId,
'password': password, 'password': password,
}; };
}
factory SignUp.fromMap(Map<String, dynamic> map) { factory SignUp.fromMap(Map<String, dynamic> map) => SignUp(
return SignUp(
sessionId: map['session_id'] as String, sessionId: map['session_id'] as String,
password: map['password'] as String, password: map['password'] as String,
); );
}
String toJson() => json.encode(toMap()); String toJson() => json.encode(toMap());
@@ -190,29 +168,23 @@ class TokenSuccess {
String? accessToken, String? accessToken,
String? refreshToken, String? refreshToken,
Account? account, Account? account,
}) { }) => TokenSuccess(
return TokenSuccess(
accessToken: accessToken ?? this.accessToken, accessToken: accessToken ?? this.accessToken,
refreshToken: refreshToken ?? this.refreshToken, refreshToken: refreshToken ?? this.refreshToken,
account: account ?? this.account, account: account ?? this.account,
); );
}
Map<String, dynamic> toMap() { Map<String, dynamic> toMap() => <String, dynamic>{
return <String, dynamic>{
'access_token': accessToken, 'access_token': accessToken,
'refresh_token': refreshToken, 'refresh_token': refreshToken,
'account': account.toMap(), 'account': account.toMap(),
}; };
}
factory TokenSuccess.fromMap(Map<String, dynamic> map) { factory TokenSuccess.fromMap(Map<String, dynamic> map) => TokenSuccess(
return TokenSuccess(
accessToken: map['access_token'] as String, accessToken: map['access_token'] as String,
refreshToken: map['refresh_token'] as String, refreshToken: map['refresh_token'] as String,
account: Account.fromMap(map['account'] as Map<String, dynamic>), account: Account.fromMap(map['account'] as Map<String, dynamic>),
); );
}
String toJson() => json.encode(toMap()); String toJson() => json.encode(toMap());
@@ -235,26 +207,20 @@ class Login {
Login copyWith({ Login copyWith({
String? email, String? email,
String? password, String? password,
}) { }) => Login(
return Login(
email: email ?? this.email, email: email ?? this.email,
password: password ?? this.password, password: password ?? this.password,
); );
}
Map<String, dynamic> toMap() { Map<String, dynamic> toMap() => <String, dynamic>{
return <String, dynamic>{
'email': email, 'email': email,
'password': password, 'password': password,
}; };
}
factory Login.fromMap(Map<String, dynamic> map) { factory Login.fromMap(Map<String, dynamic> map) => Login(
return Login(
email: map['email'] as String, email: map['email'] as String,
password: map['password'] as String, password: map['password'] as String,
); );
}
String toJson() => json.encode(toMap()); String toJson() => json.encode(toMap());
@@ -25,8 +25,6 @@ import 'package:wyatt_http_client/src/pipeline.dart';
import 'package:wyatt_http_client/src/utils/http_methods.dart'; import 'package:wyatt_http_client/src/utils/http_methods.dart';
class MiddlewareClient extends BaseClient { class MiddlewareClient extends BaseClient {
final Client inner;
final Pipeline pipeline;
MiddlewareClient({ MiddlewareClient({
Pipeline? pipeline, Pipeline? pipeline,
@@ -35,6 +33,8 @@ class MiddlewareClient extends BaseClient {
inner = inner ?? Client() { inner = inner ?? Client() {
print('Using Pipeline:\n$pipeline'); print('Using Pipeline:\n$pipeline');
} }
final Client inner;
final Pipeline pipeline;
@override @override
Future<Response> head(Uri url, {Map<String, String>? headers}) => Future<Response> head(Uri url, {Map<String, String>? headers}) =>
@@ -81,9 +81,7 @@ class MiddlewareClient extends BaseClient {
_sendUnstreamed(HttpMethods.delete.method, url, headers, body, encoding); _sendUnstreamed(HttpMethods.delete.method, url, headers, body, encoding);
@override @override
Future<StreamedResponse> send(BaseRequest request) { Future<StreamedResponse> send(BaseRequest request) => inner.send(request);
return inner.send(request);
}
Future<Response> _sendUnstreamed( Future<Response> _sendUnstreamed(
String method, String method,
@@ -23,15 +23,15 @@ import 'package:wyatt_http_client/src/utils/authentication_methods.dart';
import 'package:wyatt_http_client/src/utils/header_keys.dart'; import 'package:wyatt_http_client/src/utils/header_keys.dart';
class BasicAuthMiddleware with OnRequestMiddleware implements Middleware { class BasicAuthMiddleware with OnRequestMiddleware implements Middleware {
String? username;
String? password;
final String authenticationHeader;
BasicAuthMiddleware({ BasicAuthMiddleware({
this.username, this.username,
this.password, this.password,
this.authenticationHeader = HeaderKeys.authorization, this.authenticationHeader = HeaderKeys.authorization,
}); });
String? username;
String? password;
final String authenticationHeader;
@override @override
String getName() => 'BasicAuth'; String getName() => 'BasicAuth';
@@ -25,12 +25,6 @@ import 'package:wyatt_http_client/src/utils/http_status.dart';
class DigestAuthMiddleware class DigestAuthMiddleware
with OnRequestMiddleware, OnResponseMiddleware with OnRequestMiddleware, OnResponseMiddleware
implements Middleware { implements Middleware {
final String username;
final String password;
final DigestAuth _digestAuth;
final String authenticationHeader;
final String wwwAuthenticateHeader;
final HttpStatus unauthorized;
DigestAuthMiddleware({ DigestAuthMiddleware({
required this.username, required this.username,
@@ -39,6 +33,12 @@ class DigestAuthMiddleware
this.wwwAuthenticateHeader = HeaderKeys.wwwAuthenticate, this.wwwAuthenticateHeader = HeaderKeys.wwwAuthenticate,
this.unauthorized = HttpStatus.unauthorized, this.unauthorized = HttpStatus.unauthorized,
}) : _digestAuth = DigestAuth(username, password); }) : _digestAuth = DigestAuth(username, password);
final String username;
final String password;
final DigestAuth _digestAuth;
final String authenticationHeader;
final String wwwAuthenticateHeader;
final HttpStatus unauthorized;
@override @override
String getName() => 'DigestAuth'; String getName() => 'DigestAuth';
@@ -31,6 +31,17 @@ typedef TokenParser = String Function(Map<String, dynamic>);
class RefreshTokenAuthMiddleware class RefreshTokenAuthMiddleware
with OnRequestMiddleware, OnResponseMiddleware with OnRequestMiddleware, OnResponseMiddleware
implements Middleware { implements Middleware {
RefreshTokenAuthMiddleware({
required this.authorizationEndpoint,
required this.tokenEndpoint,
required this.accessTokenParser,
required this.refreshTokenParser,
this.authenticationHeader = HeaderKeys.authorization,
this.authenticationMethod = AuthenticationMethods.bearer,
this.unauthorized = HttpStatus.unauthorized,
this.maxAttempts = 8,
});
final String authorizationEndpoint; final String authorizationEndpoint;
final String tokenEndpoint; final String tokenEndpoint;
@@ -44,17 +55,6 @@ class RefreshTokenAuthMiddleware
final HttpStatus unauthorized; final HttpStatus unauthorized;
final int maxAttempts; final int maxAttempts;
RefreshTokenAuthMiddleware({
required this.authorizationEndpoint,
required this.tokenEndpoint,
required this.accessTokenParser,
required this.refreshTokenParser,
this.authenticationHeader = HeaderKeys.authorization,
this.authenticationMethod = AuthenticationMethods.bearer,
this.unauthorized = HttpStatus.unauthorized,
this.maxAttempts = 8,
});
@override @override
String getName() => 'RefreshToken'; String getName() => 'RefreshToken';
@@ -20,11 +20,6 @@ import 'package:wyatt_http_client/src/models/middleware_request.dart';
import 'package:wyatt_http_client/src/utils/convert.dart'; import 'package:wyatt_http_client/src/utils/convert.dart';
class UnsafeAuthMiddleware with OnRequestMiddleware implements Middleware { class UnsafeAuthMiddleware with OnRequestMiddleware implements Middleware {
String? username;
String? password;
final String usernameField;
final String passwordField;
UnsafeAuthMiddleware({ UnsafeAuthMiddleware({
this.username, this.username,
@@ -32,6 +27,11 @@ class UnsafeAuthMiddleware with OnRequestMiddleware implements Middleware {
this.usernameField = 'username', this.usernameField = 'username',
this.passwordField = 'password', this.passwordField = 'password',
}); });
String? username;
String? password;
final String usernameField;
final String passwordField;
@override @override
String getName() => 'UnsafeAuth'; String getName() => 'UnsafeAuth';
@@ -20,13 +20,13 @@ import 'package:wyatt_http_client/src/models/middleware_request.dart';
import 'package:wyatt_http_client/src/utils/protocols.dart'; import 'package:wyatt_http_client/src/utils/protocols.dart';
class UriPrefixMiddleware with OnRequestMiddleware implements Middleware { class UriPrefixMiddleware with OnRequestMiddleware implements Middleware {
final Protocols protocol;
final String? authority;
UriPrefixMiddleware({ UriPrefixMiddleware({
required this.protocol, required this.protocol,
required this.authority, required this.authority,
}); });
final Protocols protocol;
final String? authority;
@override @override
String getName() => 'UriPrefix'; String getName() => 'UriPrefix';
@@ -44,8 +44,7 @@ class MiddlewareContext {
MiddlewareRequest? lastRequest, MiddlewareRequest? lastRequest,
MiddlewareResponse? originalResponse, MiddlewareResponse? originalResponse,
MiddlewareResponse? lastResponse, MiddlewareResponse? lastResponse,
}) { }) => MiddlewareContext(
return MiddlewareContext(
pipeline: pipeline ?? this.pipeline, pipeline: pipeline ?? this.pipeline,
client: client ?? this.client, client: client ?? this.client,
originalRequest: originalRequest ?? this.originalRequest, originalRequest: originalRequest ?? this.originalRequest,
@@ -53,10 +52,7 @@ class MiddlewareContext {
originalResponse: originalResponse ?? this.originalResponse, originalResponse: originalResponse ?? this.originalResponse,
lastResponse: lastResponse ?? this.lastResponse, lastResponse: lastResponse ?? this.lastResponse,
); );
}
@override @override
String toString() { String toString() => 'MiddlewareContext(pipeline: $pipeline, client: $client, originalRequest: $originalRequest, lastRequest: $lastRequest, originalResponse: $originalResponse, lastResponse: $lastResponse)';
return 'MiddlewareContext(pipeline: $pipeline, client: $client, originalRequest: $originalRequest, lastRequest: $lastRequest, originalResponse: $originalResponse, lastResponse: $lastResponse)';
}
} }
@@ -42,22 +42,21 @@ class MiddlewareRequest {
MiddlewareRequest copyWith({ MiddlewareRequest copyWith({
UnfreezedRequest? unfreezedRequest, UnfreezedRequest? unfreezedRequest,
}) { }) =>
return MiddlewareRequest( MiddlewareRequest(
unfreezedRequest: unfreezedRequest ?? this.unfreezedRequest, unfreezedRequest: unfreezedRequest ?? this.unfreezedRequest,
); );
}
void modifyRequest(UnfreezedRequest unfreezedRequest) { void modifyRequest(UnfreezedRequest unfreezedRequest) {
String? _body; String? body;
if (unfreezedRequest.body != null) { if (unfreezedRequest.body != null) {
final body = unfreezedRequest.body; var body = unfreezedRequest.body;
if (body is String) { if (body is String) {
_body = body; body = body;
} else if (body is List) { } else if (body is List) {
_body = String.fromCharCodes(body.cast<int>()); body = String.fromCharCodes(body.cast<int>());
} else if (body is Map) { } else if (body is Map) {
_body = Convert.mapToQuery(body.cast<String, String>()); body = Convert.mapToQuery(body.cast<String, String>());
} }
} }
_httpRequest = RequestUtils.copyRequestWith( _httpRequest = RequestUtils.copyRequestWith(
@@ -65,7 +64,7 @@ class MiddlewareRequest {
method: unfreezedRequest.method, method: unfreezedRequest.method,
url: unfreezedRequest.url, url: unfreezedRequest.url,
headers: unfreezedRequest.headers, headers: unfreezedRequest.headers,
body: _body, body: body,
) as Request; ) as Request;
if (unfreezedRequest.encoding != null) { if (unfreezedRequest.encoding != null) {
_httpRequest.encoding = unfreezedRequest.encoding!; _httpRequest.encoding = unfreezedRequest.encoding!;
@@ -40,11 +40,9 @@ class MiddlewareResponse {
MiddlewareResponse copyWith({ MiddlewareResponse copyWith({
BaseResponse? httpResponse, BaseResponse? httpResponse,
}) { }) => MiddlewareResponse(
return MiddlewareResponse(
httpResponse: httpResponse ?? this.httpResponse, httpResponse: httpResponse ?? this.httpResponse,
); );
}
@override @override
String toString() => String toString() =>
@@ -17,11 +17,6 @@
import 'dart:convert'; import 'dart:convert';
class UnfreezedRequest { class UnfreezedRequest {
final String method;
final Uri url;
final Map<String, String>? headers;
final Object? body;
final Encoding? encoding;
UnfreezedRequest({ UnfreezedRequest({
required this.method, required this.method,
@@ -30,6 +25,11 @@ class UnfreezedRequest {
this.body, this.body,
this.encoding, this.encoding,
}); });
final String method;
final Uri url;
final Map<String, String>? headers;
final Object? body;
final Encoding? encoding;
UnfreezedRequest copyWith({ UnfreezedRequest copyWith({
String? method, String? method,
@@ -37,19 +37,15 @@ class UnfreezedRequest {
Map<String, String>? headers, Map<String, String>? headers,
Object? body, Object? body,
Encoding? encoding, Encoding? encoding,
}) { }) => UnfreezedRequest(
return UnfreezedRequest(
method: method ?? this.method, method: method ?? this.method,
url: url ?? this.url, url: url ?? this.url,
headers: headers ?? this.headers, headers: headers ?? this.headers,
body: body ?? this.body, body: body ?? this.body,
encoding: encoding ?? this.encoding, encoding: encoding ?? this.encoding,
); );
}
@override @override
String toString() { String toString() => 'UnfreezedRequest(method: $method, url: $url, headers: '
return 'UnfreezedRequest(method: $method, url: $url, headers: '
'$headers, body: $body, encoding: $encoding)'; '$headers, body: $body, encoding: $encoding)';
}
} }
@@ -20,13 +20,13 @@ import 'package:wyatt_http_client/src/models/middleware_request.dart';
import 'package:wyatt_http_client/src/models/middleware_response.dart'; import 'package:wyatt_http_client/src/models/middleware_response.dart';
class Pipeline { class Pipeline {
final List<Middleware> _middlewares;
int get length => _middlewares.length;
Pipeline() : _middlewares = <Middleware>[]; Pipeline() : _middlewares = <Middleware>[];
Pipeline.fromIterable(Iterable<Middleware> middlewares) Pipeline.fromIterable(Iterable<Middleware> middlewares)
: _middlewares = middlewares.toList(); : _middlewares = middlewares.toList();
final List<Middleware> _middlewares;
int get length => _middlewares.length;
/// Add a [Middleware] to this [Pipeline] /// Add a [Middleware] to this [Pipeline]
Pipeline addMiddleware(Middleware middleware) { Pipeline addMiddleware(Middleware middleware) {
@@ -21,7 +21,7 @@ class Convert {
final buffer = StringBuffer(); final buffer = StringBuffer();
for (final int part in bytes) { for (final int part in bytes) {
if (part & 0xff != part) { if (part & 0xff != part) {
throw FormatException('Non-byte integer detected'); throw const FormatException('Non-byte integer detected');
} }
buffer.write('${part < 16 ? '0' : ''}${part.toRadixString(16)}'); buffer.write('${part < 16 ? '0' : ''}${part.toRadixString(16)}');
} }
@@ -21,8 +21,8 @@ import 'package:crypto/crypto.dart';
class Crypto { class Crypto {
/// Hash a string using MD5 /// Hash a string using MD5
static String md5Hash(String data) { static String md5Hash(String data) {
final content = Utf8Encoder().convert(data); final content = const Utf8Encoder().convert(data);
final md5Crypto = md5; const md5Crypto = md5;
final digest = md5Crypto.convert(content).toString(); final digest = md5Crypto.convert(content).toString();
return digest; return digest;
} }
@@ -24,9 +24,9 @@ abstract class Delay {
return Duration.zero; return Duration.zero;
} }
final rand = Random(); final rand = Random();
final Duration delayFactor = const Duration(milliseconds: 200); const Duration delayFactor = Duration(milliseconds: 200);
final double randomizationFactor = 0.25; const double randomizationFactor = 0.25;
final Duration maxDelay = const Duration(seconds: 30); const Duration maxDelay = Duration(seconds: 30);
final rf = randomizationFactor * (rand.nextDouble() * 2 - 1) + 1; final rf = randomizationFactor * (rand.nextDouble() * 2 - 1) + 1;
final exp = min(attempt, 31); // prevent overflows. final exp = min(attempt, 31); // prevent overflows.
@@ -19,7 +19,9 @@ import 'dart:math';
import 'package:wyatt_http_client/src/utils/convert.dart'; import 'package:wyatt_http_client/src/utils/convert.dart';
import 'package:wyatt_http_client/src/utils/crypto.dart'; import 'package:wyatt_http_client/src/utils/crypto.dart';
class DigestAuth { class DigestAuth { // request counter
DigestAuth(this.username, this.password);
String username; String username;
String password; String password;
@@ -30,9 +32,7 @@ class DigestAuth {
String? _nonce; String? _nonce;
String? _opaque; String? _opaque;
int _nc = 0; // request counter int _nc = 0;
DigestAuth(this.username, this.password);
/// Splits WWW-Authenticate header into a map. /// Splits WWW-Authenticate header into a map.
Map<String, String>? splitWWWAuthenticateHeader(String header) { Map<String, String>? splitWWWAuthenticateHeader(String header) {
@@ -61,9 +61,7 @@ class DigestAuth {
return Convert.toHex(values); return Convert.toHex(values);
} }
String _formatNonceCount(int nc) { String _formatNonceCount(int nc) => nc.toRadixString(16).padLeft(8, '0');
return nc.toRadixString(16).padLeft(8, '0');
}
String _computeHA1( String _computeHA1(
String realm, String realm,
@@ -148,7 +146,7 @@ class DigestAuth {
} }
String getAuthString(String method, Uri url) { String getAuthString(String method, Uri url) {
final _cnonce = _computeNonce(); final cnonce = _computeNonce();
_nc += 1; _nc += 1;
// if url has query parameters, append query to path // if url has query parameters, append query to path
final path = url.hasQuery ? '${url.path}?${url.query}' : url.path; final path = url.hasQuery ? '${url.path}?${url.query}' : url.path;
@@ -162,7 +160,7 @@ class DigestAuth {
_qop, _qop,
_opaque, _opaque,
_realm!, _realm!,
_cnonce, cnonce,
_nonce, _nonce,
_nc, _nc,
username, username,
@@ -192,7 +190,5 @@ class DigestAuth {
} }
} }
bool isReady() { bool isReady() => _nonce != null && (_nc == 0 || _qop != null);
return _nonce != null && (_nc == 0 || _qop != null);
}
} }
@@ -95,29 +95,17 @@ enum HttpStatus {
return false; return false;
} }
bool isInfo() { bool isInfo() => statusCode >= 100 && statusCode < 200;
return statusCode >= 100 && statusCode < 200;
}
bool isSuccess() { bool isSuccess() => statusCode >= 200 && statusCode < 300;
return statusCode >= 200 && statusCode < 300;
}
bool isRedirection() { bool isRedirection() => statusCode >= 300 && statusCode < 400;
return statusCode >= 300 && statusCode < 400;
}
bool isClientError() { bool isClientError() => statusCode >= 400 && statusCode < 500;
return statusCode >= 400 && statusCode < 500;
}
bool isServerError() { bool isServerError() => statusCode >= 500 && statusCode < 600;
return statusCode >= 500 && statusCode < 600;
}
factory HttpStatus.from(int status) { factory HttpStatus.from(int status) => HttpStatus.values
return HttpStatus.values
.firstWhere((element) => element.statusCode == status); .firstWhere((element) => element.statusCode == status);
}
} }
@@ -15,9 +15,9 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
class AppError { class AppError {
final String message;
const AppError(this.message); const AppError(this.message);
final String message;
@override @override
// ignore: no_runtimetype_tostring // ignore: no_runtimetype_tostring
@@ -28,8 +28,8 @@ mixin _Left<LeftType, RightType> on _EitherBase<LeftType, RightType> {}
mixin _Right<LeftType, RightType> on _EitherBase<LeftType, RightType> {} mixin _Right<LeftType, RightType> on _EitherBase<LeftType, RightType> {}
class _EitherBaseException implements Exception { class _EitherBaseException implements Exception {
final String message;
const _EitherBaseException(this.message); const _EitherBaseException(this.message);
final String message;
@override @override
String toString() => '_EitherException: $message'; String toString() => '_EitherException: $message';
@@ -172,10 +172,10 @@ abstract class Option<T> extends _EitherBase<T, void> {
} }
class Value<T> extends Option<T> with _Left<T, void> { class Value<T> extends Option<T> with _Left<T, void> {
final T value;
/// {@macro ok} /// {@macro ok}
const Value(this.value) : super._(); const Value(this.value) : super._();
final T value;
@override @override
_EitherBase<U, void> _and<U>(_EitherBase<U, void> res) => res as Option<U>; _EitherBase<U, void> _and<U>(_EitherBase<U, void> res) => res as Option<U>;
@@ -203,10 +203,10 @@ abstract class Result<T, E> extends _EitherBase<T, E> {
/// {@macro result} /// {@macro result}
/// {@endtemplate} /// {@endtemplate}
class Ok<T, E> extends Result<T, E> with _Left<T, E> { class Ok<T, E> extends Result<T, E> with _Left<T, E> {
final T value;
/// {@macro ok} /// {@macro ok}
const Ok(this.value) : super._(); const Ok(this.value) : super._();
final T value;
@override @override
U _fold<U>(U Function(T left) fnL, U Function(E right) fnR) => fnL(value); U _fold<U>(U Function(T left) fnL, U Function(E right) fnR) => fnL(value);
@@ -268,10 +268,10 @@ class Ok<T, E> extends Result<T, E> with _Left<T, E> {
/// {@macro result} /// {@macro result}
/// {@endtemplate} /// {@endtemplate}
class Err<T, E> extends Result<T, E> with _Right<T, E> { class Err<T, E> extends Result<T, E> with _Right<T, E> {
final E error;
/// {@macro err} /// {@macro err}
const Err(this.error) : super._(); const Err(this.error) : super._();
final E error;
@override @override
U _fold<U>(U Function(T left) fnL, U Function(E right) fnR) => fnR(error); U _fold<U>(U Function(T left) fnL, U Function(E right) fnR) => fnR(error);
@@ -22,11 +22,11 @@ extension PairExtension<T> on Pair<T, T> {
/// [Pair] is a simple object which contains pair of two values. /// [Pair] is a simple object which contains pair of two values.
/// {@endtemplate} /// {@endtemplate}
class Pair<L, R> { class Pair<L, R> {
final L? left;
final R? right;
/// {@macro pair} /// {@macro pair}
const Pair(this.left, this.right); const Pair(this.left, this.right);
final L? left;
final R? right;
@override @override
String toString() => '($left, $right)'; String toString() => '($left, $right)';
@@ -19,13 +19,13 @@ import 'package:wyatt_ui_components/wyatt_wyatt_ui_components.dart';
import 'package:wyatt_ui_layout/src/presentation/layouts/layout.dart'; import 'package:wyatt_ui_layout/src/presentation/layouts/layout.dart';
class AppBarLayout extends Layout { class AppBarLayout extends Layout {
final String title;
final Widget body;
const AppBarLayout({ const AppBarLayout({
required this.title, required this.title,
required this.body, required this.body,
super.key, super.key,
}); });
final String title;
final Widget body;
@override @override
Widget build(BuildContext context) => Scaffold( Widget build(BuildContext context) => Scaffold(
@@ -3,14 +3,14 @@ import 'package:wyatt_ui_components/wyatt_wyatt_ui_components.dart';
import 'package:wyatt_ui_layout/src/presentation/layouts/layout.dart'; import 'package:wyatt_ui_layout/src/presentation/layouts/layout.dart';
class BottomNavigationBarLayout extends Layout { class BottomNavigationBarLayout extends Layout {
final Widget body;
final int currentIndex;
const BottomNavigationBarLayout({ const BottomNavigationBarLayout({
required this.currentIndex, required this.currentIndex,
required this.body, required this.body,
super.key, super.key,
}); });
final Widget body;
final int currentIndex;
@override @override
Widget build(BuildContext context) => Scaffold( Widget build(BuildContext context) => Scaffold(
body: body, body: body,
@@ -19,9 +19,6 @@ import 'package:wyatt_ui_components/wyatt_wyatt_ui_components.dart';
import 'package:wyatt_ui_layout/src/presentation/layouts/layout.dart'; import 'package:wyatt_ui_layout/src/presentation/layouts/layout.dart';
class FrameLayout extends Layout { class FrameLayout extends Layout {
final String title;
final Widget body;
final int currentIndex;
const FrameLayout({ const FrameLayout({
required this.title, required this.title,
@@ -29,6 +26,9 @@ class FrameLayout extends Layout {
required this.currentIndex, required this.currentIndex,
super.key, super.key,
}); });
final String title;
final Widget body;
final int currentIndex;
@override @override
Widget build(BuildContext context) => Scaffold( Widget build(BuildContext context) => Scaffold(