From 3a136e1d3694f71ff314fc5a8e0a49e79d3ebb61 Mon Sep 17 00:00:00 2001 From: Amanda Shafack Date: Thu, 23 Feb 2023 03:41:40 +0100 Subject: [PATCH] feat: implement authentication with twitter - Add twitter authentication configurations on android and ios - Implement the overall twitter authentication logic - Attach the twitter authentication logic to the user interface --- android/app/src/main/AndroidManifest.xml | 9 + ios/Runner/Info.plist | 18 + .../facebook_auth_repository.dart | 2 +- .../twitter_authentication_bloc.dart | 49 ++ .../twitter_authentication_bloc.freezed.dart | 555 ++++++++++++++++++ .../twitter_authentication_event.dart | 8 + .../twitter_authentication_state.dart | 13 + .../domain/i_twitter_repository_facade.dart | 8 + .../twitter_auth_repository.dart | 77 +++ lib/injection.config.dart | 92 +-- .../view/widgets/alternative_auth.dart | 7 +- lib/sign_in/view/sign_in_page.dart | 4 + macos/Flutter/GeneratedPluginRegistrant.swift | 2 + pubspec.yaml | 1 + 14 files changed, 800 insertions(+), 45 deletions(-) create mode 100644 lib/authentication_with_twitter/application/twitter_authentication/twitter_authentication_bloc.dart create mode 100644 lib/authentication_with_twitter/application/twitter_authentication/twitter_authentication_bloc.freezed.dart create mode 100644 lib/authentication_with_twitter/application/twitter_authentication/twitter_authentication_event.dart create mode 100644 lib/authentication_with_twitter/application/twitter_authentication/twitter_authentication_state.dart create mode 100644 lib/authentication_with_twitter/domain/i_twitter_repository_facade.dart create mode 100644 lib/authentication_with_twitter/infrastructure/twitter_auth_repository.dart diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index e95f462..2a064ee 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -25,6 +25,15 @@ + + + + + + + + diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index a4705b3..370ec1c 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -29,6 +29,7 @@ ???? CFBundleVersion $(FLUTTER_BUILD_NUMBER) + CFBundleURLTypes @@ -49,6 +50,23 @@ fbapi fb-messenger-share-api + + + CFBundleURLTypes + + + CFBundleTypeRole + Editor + CFBundleURLName + + CFBundleURLSchemes + + + example + + + + LSRequiresIPhoneOS UILaunchStoryboardName diff --git a/lib/authentication_with_facebook/infrastructure/facebook_auth_repository.dart b/lib/authentication_with_facebook/infrastructure/facebook_auth_repository.dart index 1a05cc1..72c2717 100644 --- a/lib/authentication_with_facebook/infrastructure/facebook_auth_repository.dart +++ b/lib/authentication_with_facebook/infrastructure/facebook_auth_repository.dart @@ -35,7 +35,7 @@ class FacebookAuthenticationRepository implements IFacebookRepositoryFacade { if (user == null) { return left(const AuthFailure.userNotFound()); } - + return right(UserDTO.fromFirebase(user).toDomain()); } on SocketException catch (e) { return left(AuthFailure.fromErrorMessage(e.message)); diff --git a/lib/authentication_with_twitter/application/twitter_authentication/twitter_authentication_bloc.dart b/lib/authentication_with_twitter/application/twitter_authentication/twitter_authentication_bloc.dart new file mode 100644 index 0000000..f038bd1 --- /dev/null +++ b/lib/authentication_with_twitter/application/twitter_authentication/twitter_authentication_bloc.dart @@ -0,0 +1,49 @@ +import 'package:bloc/bloc.dart'; +import 'package:dartz/dartz.dart'; +import 'package:fpb/authentication_with_twitter/domain/i_twitter_repository_facade.dart'; +import 'package:fpb/core/domain/user.dart'; +import 'package:fpb/core/failures/auth_failure.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'twitter_authentication_event.dart'; +part 'twitter_authentication_state.dart'; +part 'twitter_authentication_bloc.freezed.dart'; + +class TwitterAuthenticationBloc + extends Bloc { + TwitterAuthenticationBloc({required this.authenticationRepository}) + : super(TwitterAuthenticationState.initial()) { + on(_onTwitterSignInPressed); + on(_onTwitterSignOutPressed); + } + + final ITwitterRepositoryFacade authenticationRepository; + + Future _onTwitterSignInPressed( + TwitterSignIn event, + Emitter emit, + ) async { + emit(state.copyWith(isLoading: true)); + final failureOrUser = await authenticationRepository.signInWithTwitter(); + failureOrUser.fold( + (failure) => emit(TwitterAuthenticationState( + failureOrUser: left(failure), isLoading: false)), + (user) => emit(TwitterAuthenticationState( + isLoading: false, failureOrUser: right(user))), + ); + } + + Future _onTwitterSignOutPressed( + TwitterSignOut event, + Emitter emit, + ) async { + emit(state.copyWith(isLoading: true)); + final failureOrUnit = await authenticationRepository.signOut(); + failureOrUnit.fold( + (failure) => emit(TwitterAuthenticationState( + failureOrUser: left(failure), isLoading: false)), + (unit) => emit(TwitterAuthenticationState( + isLoading: false, failureOrUser: right(User.empty))), + ); + } +} diff --git a/lib/authentication_with_twitter/application/twitter_authentication/twitter_authentication_bloc.freezed.dart b/lib/authentication_with_twitter/application/twitter_authentication/twitter_authentication_bloc.freezed.dart new file mode 100644 index 0000000..0b23c80 --- /dev/null +++ b/lib/authentication_with_twitter/application/twitter_authentication/twitter_authentication_bloc.freezed.dart @@ -0,0 +1,555 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target + +part of 'twitter_authentication_bloc.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); + +/// @nodoc +mixin _$TwitterAuthenticationEvent { + @optionalTypeArgs + TResult when({ + required TResult Function() started, + required TResult Function() signIn, + required TResult Function() signOut, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function()? started, + TResult? Function()? signIn, + TResult? Function()? signOut, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? started, + TResult Function()? signIn, + TResult Function()? signOut, + required TResult orElse(), + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult map({ + required TResult Function(_Started value) started, + required TResult Function(TwitterSignIn value) signIn, + required TResult Function(TwitterSignOut value) signOut, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(_Started value)? started, + TResult? Function(TwitterSignIn value)? signIn, + TResult? Function(TwitterSignOut value)? signOut, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult maybeMap({ + TResult Function(_Started value)? started, + TResult Function(TwitterSignIn value)? signIn, + TResult Function(TwitterSignOut value)? signOut, + required TResult orElse(), + }) => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $TwitterAuthenticationEventCopyWith<$Res> { + factory $TwitterAuthenticationEventCopyWith(TwitterAuthenticationEvent value, + $Res Function(TwitterAuthenticationEvent) then) = + _$TwitterAuthenticationEventCopyWithImpl<$Res, + TwitterAuthenticationEvent>; +} + +/// @nodoc +class _$TwitterAuthenticationEventCopyWithImpl<$Res, + $Val extends TwitterAuthenticationEvent> + implements $TwitterAuthenticationEventCopyWith<$Res> { + _$TwitterAuthenticationEventCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; +} + +/// @nodoc +abstract class _$$_StartedCopyWith<$Res> { + factory _$$_StartedCopyWith( + _$_Started value, $Res Function(_$_Started) then) = + __$$_StartedCopyWithImpl<$Res>; +} + +/// @nodoc +class __$$_StartedCopyWithImpl<$Res> + extends _$TwitterAuthenticationEventCopyWithImpl<$Res, _$_Started> + implements _$$_StartedCopyWith<$Res> { + __$$_StartedCopyWithImpl(_$_Started _value, $Res Function(_$_Started) _then) + : super(_value, _then); +} + +/// @nodoc + +class _$_Started implements _Started { + const _$_Started(); + + @override + String toString() { + return 'TwitterAuthenticationEvent.started()'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other.runtimeType == runtimeType && other is _$_Started); + } + + @override + int get hashCode => runtimeType.hashCode; + + @override + @optionalTypeArgs + TResult when({ + required TResult Function() started, + required TResult Function() signIn, + required TResult Function() signOut, + }) { + return started(); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function()? started, + TResult? Function()? signIn, + TResult? Function()? signOut, + }) { + return started?.call(); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? started, + TResult Function()? signIn, + TResult Function()? signOut, + required TResult orElse(), + }) { + if (started != null) { + return started(); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(_Started value) started, + required TResult Function(TwitterSignIn value) signIn, + required TResult Function(TwitterSignOut value) signOut, + }) { + return started(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(_Started value)? started, + TResult? Function(TwitterSignIn value)? signIn, + TResult? Function(TwitterSignOut value)? signOut, + }) { + return started?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(_Started value)? started, + TResult Function(TwitterSignIn value)? signIn, + TResult Function(TwitterSignOut value)? signOut, + required TResult orElse(), + }) { + if (started != null) { + return started(this); + } + return orElse(); + } +} + +abstract class _Started implements TwitterAuthenticationEvent { + const factory _Started() = _$_Started; +} + +/// @nodoc +abstract class _$$TwitterSignInCopyWith<$Res> { + factory _$$TwitterSignInCopyWith( + _$TwitterSignIn value, $Res Function(_$TwitterSignIn) then) = + __$$TwitterSignInCopyWithImpl<$Res>; +} + +/// @nodoc +class __$$TwitterSignInCopyWithImpl<$Res> + extends _$TwitterAuthenticationEventCopyWithImpl<$Res, _$TwitterSignIn> + implements _$$TwitterSignInCopyWith<$Res> { + __$$TwitterSignInCopyWithImpl( + _$TwitterSignIn _value, $Res Function(_$TwitterSignIn) _then) + : super(_value, _then); +} + +/// @nodoc + +class _$TwitterSignIn implements TwitterSignIn { + _$TwitterSignIn(); + + @override + String toString() { + return 'TwitterAuthenticationEvent.signIn()'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other.runtimeType == runtimeType && other is _$TwitterSignIn); + } + + @override + int get hashCode => runtimeType.hashCode; + + @override + @optionalTypeArgs + TResult when({ + required TResult Function() started, + required TResult Function() signIn, + required TResult Function() signOut, + }) { + return signIn(); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function()? started, + TResult? Function()? signIn, + TResult? Function()? signOut, + }) { + return signIn?.call(); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? started, + TResult Function()? signIn, + TResult Function()? signOut, + required TResult orElse(), + }) { + if (signIn != null) { + return signIn(); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(_Started value) started, + required TResult Function(TwitterSignIn value) signIn, + required TResult Function(TwitterSignOut value) signOut, + }) { + return signIn(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(_Started value)? started, + TResult? Function(TwitterSignIn value)? signIn, + TResult? Function(TwitterSignOut value)? signOut, + }) { + return signIn?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(_Started value)? started, + TResult Function(TwitterSignIn value)? signIn, + TResult Function(TwitterSignOut value)? signOut, + required TResult orElse(), + }) { + if (signIn != null) { + return signIn(this); + } + return orElse(); + } +} + +abstract class TwitterSignIn implements TwitterAuthenticationEvent { + factory TwitterSignIn() = _$TwitterSignIn; +} + +/// @nodoc +abstract class _$$TwitterSignOutCopyWith<$Res> { + factory _$$TwitterSignOutCopyWith( + _$TwitterSignOut value, $Res Function(_$TwitterSignOut) then) = + __$$TwitterSignOutCopyWithImpl<$Res>; +} + +/// @nodoc +class __$$TwitterSignOutCopyWithImpl<$Res> + extends _$TwitterAuthenticationEventCopyWithImpl<$Res, _$TwitterSignOut> + implements _$$TwitterSignOutCopyWith<$Res> { + __$$TwitterSignOutCopyWithImpl( + _$TwitterSignOut _value, $Res Function(_$TwitterSignOut) _then) + : super(_value, _then); +} + +/// @nodoc + +class _$TwitterSignOut implements TwitterSignOut { + _$TwitterSignOut(); + + @override + String toString() { + return 'TwitterAuthenticationEvent.signOut()'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other.runtimeType == runtimeType && other is _$TwitterSignOut); + } + + @override + int get hashCode => runtimeType.hashCode; + + @override + @optionalTypeArgs + TResult when({ + required TResult Function() started, + required TResult Function() signIn, + required TResult Function() signOut, + }) { + return signOut(); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function()? started, + TResult? Function()? signIn, + TResult? Function()? signOut, + }) { + return signOut?.call(); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? started, + TResult Function()? signIn, + TResult Function()? signOut, + required TResult orElse(), + }) { + if (signOut != null) { + return signOut(); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(_Started value) started, + required TResult Function(TwitterSignIn value) signIn, + required TResult Function(TwitterSignOut value) signOut, + }) { + return signOut(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(_Started value)? started, + TResult? Function(TwitterSignIn value)? signIn, + TResult? Function(TwitterSignOut value)? signOut, + }) { + return signOut?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(_Started value)? started, + TResult Function(TwitterSignIn value)? signIn, + TResult Function(TwitterSignOut value)? signOut, + required TResult orElse(), + }) { + if (signOut != null) { + return signOut(this); + } + return orElse(); + } +} + +abstract class TwitterSignOut implements TwitterAuthenticationEvent { + factory TwitterSignOut() = _$TwitterSignOut; +} + +/// @nodoc +mixin _$TwitterAuthenticationState { + bool get isLoading => throw _privateConstructorUsedError; + Either get failureOrUser => + throw _privateConstructorUsedError; + + @JsonKey(ignore: true) + $TwitterAuthenticationStateCopyWith + get copyWith => throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $TwitterAuthenticationStateCopyWith<$Res> { + factory $TwitterAuthenticationStateCopyWith(TwitterAuthenticationState value, + $Res Function(TwitterAuthenticationState) then) = + _$TwitterAuthenticationStateCopyWithImpl<$Res, + TwitterAuthenticationState>; + @useResult + $Res call({bool isLoading, Either failureOrUser}); +} + +/// @nodoc +class _$TwitterAuthenticationStateCopyWithImpl<$Res, + $Val extends TwitterAuthenticationState> + implements $TwitterAuthenticationStateCopyWith<$Res> { + _$TwitterAuthenticationStateCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? isLoading = null, + Object? failureOrUser = null, + }) { + return _then(_value.copyWith( + isLoading: null == isLoading + ? _value.isLoading + : isLoading // ignore: cast_nullable_to_non_nullable + as bool, + failureOrUser: null == failureOrUser + ? _value.failureOrUser + : failureOrUser // ignore: cast_nullable_to_non_nullable + as Either, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$_TwitterAuthenticationStateCopyWith<$Res> + implements $TwitterAuthenticationStateCopyWith<$Res> { + factory _$$_TwitterAuthenticationStateCopyWith( + _$_TwitterAuthenticationState value, + $Res Function(_$_TwitterAuthenticationState) then) = + __$$_TwitterAuthenticationStateCopyWithImpl<$Res>; + @override + @useResult + $Res call({bool isLoading, Either failureOrUser}); +} + +/// @nodoc +class __$$_TwitterAuthenticationStateCopyWithImpl<$Res> + extends _$TwitterAuthenticationStateCopyWithImpl<$Res, + _$_TwitterAuthenticationState> + implements _$$_TwitterAuthenticationStateCopyWith<$Res> { + __$$_TwitterAuthenticationStateCopyWithImpl( + _$_TwitterAuthenticationState _value, + $Res Function(_$_TwitterAuthenticationState) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? isLoading = null, + Object? failureOrUser = null, + }) { + return _then(_$_TwitterAuthenticationState( + isLoading: null == isLoading + ? _value.isLoading + : isLoading // ignore: cast_nullable_to_non_nullable + as bool, + failureOrUser: null == failureOrUser + ? _value.failureOrUser + : failureOrUser // ignore: cast_nullable_to_non_nullable + as Either, + )); + } +} + +/// @nodoc + +class _$_TwitterAuthenticationState implements _TwitterAuthenticationState { + const _$_TwitterAuthenticationState( + {this.isLoading = false, this.failureOrUser = const Right(User.empty)}); + + @override + @JsonKey() + final bool isLoading; + @override + @JsonKey() + final Either failureOrUser; + + @override + String toString() { + return 'TwitterAuthenticationState(isLoading: $isLoading, failureOrUser: $failureOrUser)'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$_TwitterAuthenticationState && + (identical(other.isLoading, isLoading) || + other.isLoading == isLoading) && + (identical(other.failureOrUser, failureOrUser) || + other.failureOrUser == failureOrUser)); + } + + @override + int get hashCode => Object.hash(runtimeType, isLoading, failureOrUser); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$_TwitterAuthenticationStateCopyWith<_$_TwitterAuthenticationState> + get copyWith => __$$_TwitterAuthenticationStateCopyWithImpl< + _$_TwitterAuthenticationState>(this, _$identity); +} + +abstract class _TwitterAuthenticationState + implements TwitterAuthenticationState { + const factory _TwitterAuthenticationState( + {final bool isLoading, + final Either failureOrUser}) = + _$_TwitterAuthenticationState; + + @override + bool get isLoading; + @override + Either get failureOrUser; + @override + @JsonKey(ignore: true) + _$$_TwitterAuthenticationStateCopyWith<_$_TwitterAuthenticationState> + get copyWith => throw _privateConstructorUsedError; +} diff --git a/lib/authentication_with_twitter/application/twitter_authentication/twitter_authentication_event.dart b/lib/authentication_with_twitter/application/twitter_authentication/twitter_authentication_event.dart new file mode 100644 index 0000000..436e721 --- /dev/null +++ b/lib/authentication_with_twitter/application/twitter_authentication/twitter_authentication_event.dart @@ -0,0 +1,8 @@ +part of 'twitter_authentication_bloc.dart'; + +@freezed +class TwitterAuthenticationEvent with _$TwitterAuthenticationEvent { + const factory TwitterAuthenticationEvent.started() = _Started; + factory TwitterAuthenticationEvent.signIn() = TwitterSignIn; + factory TwitterAuthenticationEvent.signOut() = TwitterSignOut; +} diff --git a/lib/authentication_with_twitter/application/twitter_authentication/twitter_authentication_state.dart b/lib/authentication_with_twitter/application/twitter_authentication/twitter_authentication_state.dart new file mode 100644 index 0000000..61e49fa --- /dev/null +++ b/lib/authentication_with_twitter/application/twitter_authentication/twitter_authentication_state.dart @@ -0,0 +1,13 @@ +// ignore_for_file: inference_failure_on_instance_creation + +part of 'twitter_authentication_bloc.dart'; + +@freezed +class TwitterAuthenticationState with _$TwitterAuthenticationState { + const factory TwitterAuthenticationState({ + @Default(false) bool isLoading, + @Default(Right(User.empty)) Either failureOrUser, + }) = _TwitterAuthenticationState; + + factory TwitterAuthenticationState.initial() => TwitterAuthenticationState(); +} diff --git a/lib/authentication_with_twitter/domain/i_twitter_repository_facade.dart b/lib/authentication_with_twitter/domain/i_twitter_repository_facade.dart new file mode 100644 index 0000000..3f30531 --- /dev/null +++ b/lib/authentication_with_twitter/domain/i_twitter_repository_facade.dart @@ -0,0 +1,8 @@ +import 'package:dartz/dartz.dart'; +import 'package:fpb/core/domain/user.dart'; +import 'package:fpb/core/failures/auth_failure.dart'; + +abstract class ITwitterRepositoryFacade { + Future> signInWithTwitter(); + Future> signOut(); +} diff --git a/lib/authentication_with_twitter/infrastructure/twitter_auth_repository.dart b/lib/authentication_with_twitter/infrastructure/twitter_auth_repository.dart new file mode 100644 index 0000000..6bba13a --- /dev/null +++ b/lib/authentication_with_twitter/infrastructure/twitter_auth_repository.dart @@ -0,0 +1,77 @@ +import 'dart:io'; + +import 'package:dartz/dartz.dart'; +import 'package:firebase_auth/firebase_auth.dart' hide User; +import 'package:flutter/services.dart'; +import 'package:fpb/authentication_with_twitter/domain/i_twitter_repository_facade.dart'; +import 'package:fpb/core/domain/user.dart'; +import 'package:fpb/core/failures/auth_failure.dart'; +import 'package:fpb/core/infrastructure/user.dto.dart'; +import 'package:injectable/injectable.dart'; +import 'package:twitter_login/twitter_login.dart'; + +@LazySingleton(as: ITwitterRepositoryFacade) +class TwitterAuthenticationRepository implements ITwitterRepositoryFacade { + final FirebaseAuth _firebaseAuth; + + TwitterAuthenticationRepository(this._firebaseAuth); + + @override + Future> signInWithTwitter() async { + try { + // Create a TwitterLogin instance + final twitterLogin = new TwitterLogin( + apiKey: '', + apiSecretKey: ' ', + redirectURI: '://'); + + // Trigger the sign-in flow + final authResult = await twitterLogin.login(); + + // Create a credential from the access token + final twitterAuthCredential = TwitterAuthProvider.credential( + accessToken: authResult.authToken!, + secret: authResult.authTokenSecret!, + ); + + final userCredential = + await _firebaseAuth.signInWithCredential(twitterAuthCredential); + + final user = userCredential.user; + + if (user == null) { + return left(const AuthFailure.userNotFound()); + } + + return right(UserDTO.fromFirebase(user).toDomain()); + } on SocketException catch (e) { + return left(AuthFailure.fromErrorMessage(e.message)); + } on PlatformException catch (e) { + return left(AuthFailure.fromErrorMessage(e.code)); + } on FirebaseAuthException catch (e) { + return left(AuthFailure.fromErrorMessage(e.code)); + } on FirebaseException catch (e) { + return left(AuthFailure.fromErrorMessage(e.code)); + } + } + + @override + Future> signOut() async { + try { + await Future.wait([ + _firebaseAuth.signOut(), + // _facebookAuth.logOut(), + ]); + + return right(unit); + } on SocketException catch (e) { + return left(AuthFailure.fromErrorMessage(e.message)); + } on PlatformException catch (e) { + return left(AuthFailure.fromErrorMessage(e.code)); + } on FirebaseAuthException catch (e) { + return left(AuthFailure.fromErrorMessage(e.code)); + } on FirebaseException catch (e) { + return left(AuthFailure.fromErrorMessage(e.code)); + } + } +} diff --git a/lib/injection.config.dart b/lib/injection.config.dart index 41bc4d3..3c8ba1d 100644 --- a/lib/injection.config.dart +++ b/lib/injection.config.dart @@ -13,49 +13,53 @@ import 'package:firebase_auth/firebase_auth.dart' as _i7; import 'package:firebase_core/firebase_core.dart' as _i6; import 'package:flutter_facebook_auth/flutter_facebook_auth.dart' as _i5; import 'package:fpb/authentication_mock_without_backend/application/bloc/authentication_bloc.dart' - as _i17; + as _i19; import 'package:fpb/authentication_mock_without_backend/infrastructure/authentication_mock_module_injection.dart' - as _i30; + as _i32; import 'package:fpb/authentication_with_facebook/application/facebook_auth_bloc.dart' - as _i19; + as _i21; import 'package:fpb/authentication_with_facebook/domain/i_facebook_repository_facade.dart' as _i10; import 'package:fpb/authentication_with_facebook/infrastructure/facebook_auth_repository.dart' as _i11; import 'package:fpb/authentication_with_facebook/infrastructure/facebook_authentication_injectable_module.dart' - as _i32; + as _i34; import 'package:fpb/authentication_with_firebase/application/bloc/auth_bloc.dart' - as _i24; + as _i26; import 'package:fpb/authentication_with_firebase/domain/i_auth_facade.dart' - as _i21; + as _i23; import 'package:fpb/authentication_with_firebase/infrastructure/firebase_auth_facade_impl.dart' - as _i22; + as _i24; import 'package:fpb/authentication_with_firebase/infrastructure/firebase_auth_injectable_module.dart' - as _i31; + as _i33; import 'package:fpb/authentication_with_google/application/google_auth_bloc/google_sign_in_bloc.dart' - as _i20; + as _i22; import 'package:fpb/authentication_with_google/domain/i_google_repository_facade.dart' as _i12; import 'package:fpb/authentication_with_google/infrastructure/google_authentication_injectable_module.dart' - as _i28; + as _i30; import 'package:fpb/authentication_with_google/infrastructure/google_authentication_repository.dart' as _i13; +import 'package:fpb/authentication_with_twitter/domain/i_twitter_repository_facade.dart' + as _i14; +import 'package:fpb/authentication_with_twitter/infrastructure/twitter_auth_repository.dart' + as _i15; import 'package:fpb/core/application/email_password_bloc/email_password_bloc.dart' - as _i25; -import 'package:fpb/core/application/internet_and_time_bloc/internet_and_time_bloc.dart' as _i27; -import 'package:fpb/core/infrastructure/core_injectable_module.dart' as _i29; -import 'package:fpb/core/settings/app_settings_helper.dart' as _i23; -import 'package:fpb/core/settings/cached.dart' as _i18; +import 'package:fpb/core/application/internet_and_time_bloc/internet_and_time_bloc.dart' + as _i29; +import 'package:fpb/core/infrastructure/core_injectable_module.dart' as _i31; +import 'package:fpb/core/settings/app_settings_helper.dart' as _i25; +import 'package:fpb/core/settings/cached.dart' as _i20; import 'package:fpb/home/application/home_view_bloc/home_view_bloc.dart' - as _i26; + as _i28; import 'package:get_it/get_it.dart' as _i1; import 'package:google_sign_in/google_sign_in.dart' as _i9; import 'package:injectable/injectable.dart' as _i2; -import 'package:ntp/ntp.dart' as _i14; -import 'package:shared_preferences/shared_preferences.dart' as _i15; +import 'package:ntp/ntp.dart' as _i16; +import 'package:shared_preferences/shared_preferences.dart' as _i17; import 'package:user_repository/user_repository.dart' - as _i16; // ignore_for_file: unnecessary_lambdas + as _i18; // ignore_for_file: unnecessary_lambdas // ignore_for_file: lines_longer_than_80_chars extension GetItInjectableX on _i1.GetIt { @@ -103,51 +107,53 @@ extension GetItInjectableX on _i1.GetIt { gh<_i9.GoogleSignIn>(), gh<_i7.FirebaseAuth>(), )); - gh.lazySingleton<_i14.NTP>(() => coreInjectableModule.ntp); - await gh.factoryAsync<_i15.SharedPreferences>( + gh.lazySingleton<_i14.ITwitterRepositoryFacade>( + () => _i15.TwitterAuthenticationRepository(gh<_i7.FirebaseAuth>())); + gh.lazySingleton<_i16.NTP>(() => coreInjectableModule.ntp); + await gh.factoryAsync<_i17.SharedPreferences>( () => firebaseAuthInjectableModule.sharePreferences, preResolve: true, ); - gh.singleton<_i16.UserRepository>( + gh.singleton<_i18.UserRepository>( authenticationMockModuleInjection.userRepository); - gh.factory<_i17.AuthenticationBloc>(() => _i17.AuthenticationBloc( + gh.factory<_i19.AuthenticationBloc>(() => _i19.AuthenticationBloc( authenticationRepository: gh<_i3.AuthenticationRepository>(), - userRepository: gh<_i16.UserRepository>(), + userRepository: gh<_i18.UserRepository>(), )); - gh.singleton<_i18.Cached>(_i18.Cached(gh<_i15.SharedPreferences>())); - gh.factory<_i19.FacebookAuthBloc>(() => _i19.FacebookAuthBloc( + gh.singleton<_i20.Cached>(_i20.Cached(gh<_i17.SharedPreferences>())); + gh.factory<_i21.FacebookAuthBloc>(() => _i21.FacebookAuthBloc( authenticationRepository: gh<_i10.IFacebookRepositoryFacade>())); - gh.factory<_i20.GoogleSignInBloc>(() => _i20.GoogleSignInBloc( + gh.factory<_i22.GoogleSignInBloc>(() => _i22.GoogleSignInBloc( authenticationRepository: gh<_i12.IGoogleRepositoryFacade>())); - gh.lazySingleton<_i21.IAuthFacade>(() => _i22.FirebaseAuthFacade( + gh.lazySingleton<_i23.IAuthFacade>(() => _i24.FirebaseAuthFacade( gh<_i7.FirebaseAuth>(), - gh<_i18.Cached>(), + gh<_i20.Cached>(), )); - gh.lazySingleton<_i23.AppSettingsHelper>(() => _i23.AppSettingsHelper( - gh<_i18.Cached>(), + gh.lazySingleton<_i25.AppSettingsHelper>(() => _i25.AppSettingsHelper( + gh<_i20.Cached>(), gh<_i4.Connectivity>(), )); - gh.factory<_i24.AuthBloc>(() => _i24.AuthBloc(gh<_i21.IAuthFacade>())); - gh.singleton<_i25.EmailPasswordBloc>(_i25.EmailPasswordBloc( - authenticationRepository: gh<_i21.IAuthFacade>())); - gh.factory<_i26.HomeViewBloc>( - () => _i26.HomeViewBloc(gh<_i23.AppSettingsHelper>())); - gh.factory<_i27.InternetAndTimeBloc>( - () => _i27.InternetAndTimeBloc(gh<_i23.AppSettingsHelper>())); + gh.factory<_i26.AuthBloc>(() => _i26.AuthBloc(gh<_i23.IAuthFacade>())); + gh.singleton<_i27.EmailPasswordBloc>(_i27.EmailPasswordBloc( + authenticationRepository: gh<_i23.IAuthFacade>())); + gh.factory<_i28.HomeViewBloc>( + () => _i28.HomeViewBloc(gh<_i25.AppSettingsHelper>())); + gh.factory<_i29.InternetAndTimeBloc>( + () => _i29.InternetAndTimeBloc(gh<_i25.AppSettingsHelper>())); return this; } } class _$GoogleAuthenticationInjectableModule - extends _i28.GoogleAuthenticationInjectableModule {} + extends _i30.GoogleAuthenticationInjectableModule {} -class _$CoreInjectableModule extends _i29.CoreInjectableModule {} +class _$CoreInjectableModule extends _i31.CoreInjectableModule {} class _$AuthenticationMockModuleInjection - extends _i30.AuthenticationMockModuleInjection {} + extends _i32.AuthenticationMockModuleInjection {} class _$FirebaseAuthInjectableModule - extends _i31.FirebaseAuthInjectableModule {} + extends _i33.FirebaseAuthInjectableModule {} class _$FacebookAuthenticationInjectableModule - extends _i32.FacebookAuthenticationInjectableModule {} + extends _i34.FacebookAuthenticationInjectableModule {} diff --git a/lib/onboarding/view/widgets/alternative_auth.dart b/lib/onboarding/view/widgets/alternative_auth.dart index 2d87d4d..7d96daa 100644 --- a/lib/onboarding/view/widgets/alternative_auth.dart +++ b/lib/onboarding/view/widgets/alternative_auth.dart @@ -3,6 +3,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:fpb/assets/fpb_svg.dart'; import 'package:fpb/authentication_with_facebook/application/facebook_auth_bloc.dart'; import 'package:fpb/authentication_with_google/application/google_auth_bloc/google_sign_in_bloc.dart'; +import 'package:fpb/authentication_with_twitter/application/twitter_authentication/twitter_authentication_bloc.dart'; import 'package:fpb/core/presentation/widget/icon_login.dart'; class AlternativeAuth extends StatelessWidget { @@ -35,7 +36,11 @@ class AlternativeAuth extends StatelessWidget { IconLogin( svg: SvgNames.twitter, box: box, - onTap: () {}, + onTap: () { + context + .read() + .add(TwitterAuthenticationEvent.signIn()); + }, ), IconLogin( svg: SvgNames.apple, diff --git a/lib/sign_in/view/sign_in_page.dart b/lib/sign_in/view/sign_in_page.dart index edfb6f0..2bdf5af 100644 --- a/lib/sign_in/view/sign_in_page.dart +++ b/lib/sign_in/view/sign_in_page.dart @@ -7,6 +7,7 @@ import 'package:fpb/assets/fpb_icons/fpb_icons_icons.dart'; import 'package:fpb/assets/fpb_svg.dart'; import 'package:fpb/authentication_with_facebook/application/facebook_auth_bloc.dart'; import 'package:fpb/authentication_with_google/application/google_auth_bloc/google_sign_in_bloc.dart'; +import 'package:fpb/authentication_with_twitter/application/twitter_authentication/twitter_authentication_bloc.dart'; import 'package:fpb/core/application/email_password_bloc/email_password_bloc.dart'; import 'package:fpb/core/presentation/extension/extensions.dart'; import 'package:fpb/core/shared/helpers/is_keyboard_visible.dart'; @@ -32,6 +33,9 @@ class SignInScreen extends StatelessWidget { BlocProvider( create: (context) => getIt(), ), + BlocProvider( + create: (context) => getIt(), + ), BlocProvider( create: (context) => getIt(), ), diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 4a243f5..1774575 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -13,6 +13,7 @@ import firebase_core import flutter_secure_storage_macos import path_provider_foundation import shared_preferences_foundation +import twitter_login func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { FLTFirebaseFirestorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseFirestorePlugin")) @@ -23,4 +24,5 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) + TwitterLoginPlugin.register(with: registry.registrar(forPlugin: "TwitterLoginPlugin")) } diff --git a/pubspec.yaml b/pubspec.yaml index 4afa9ab..8edb0c0 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -44,6 +44,7 @@ dependencies: auto_route: ^5.0.4 google_fonts: ^4.0.3 flutter_facebook_auth: ^5.0.7 + twitter_login: ^4.3.1 dev_dependencies: widgetbook_generator: