diff --git a/.idea/appflowy_client.iml b/.idea/appflowy_client.iml index 07a773d539..1e204770fb 100644 --- a/.idea/appflowy_client.iml +++ b/.idea/appflowy_client.iml @@ -78,6 +78,9 @@ + + + diff --git a/app_flowy/lib/startup/deps_inject/prelude.dart b/app_flowy/lib/startup/deps_inject/prelude.dart index 945c492cd9..3811e93bd0 100644 --- a/app_flowy/lib/startup/deps_inject/prelude.dart +++ b/app_flowy/lib/startup/deps_inject/prelude.dart @@ -1,5 +1,6 @@ import 'package:app_flowy/startup/launch.dart'; import 'package:app_flowy/startup/startup.dart'; +import 'package:app_flowy/user/infrastructure/interface_impl.dart'; import 'package:app_flowy/welcome/infrastructure/interface_impl.dart'; import 'package:flowy_sdk/flowy_sdk.dart'; import 'package:get_it/get_it.dart'; @@ -13,5 +14,6 @@ Future initGetIt( getIt.registerLazySingleton(() => const FlowySDK()); getIt.registerLazySingleton(() => AppLauncher(env, getIt)); - await Welcome.dependencyResolved(getIt); + await WelcomeDepsResolver.resolve(getIt); + await UserDepsResolver.resolve(getIt); } diff --git a/app_flowy/lib/user/application/sign_in/sign_in_bloc.dart b/app_flowy/lib/user/application/sign_in/sign_in_bloc.dart index e69de29bb2..f1c4de04ec 100644 --- a/app_flowy/lib/user/application/sign_in/sign_in_bloc.dart +++ b/app_flowy/lib/user/application/sign_in/sign_in_bloc.dart @@ -0,0 +1,46 @@ +import 'package:app_flowy/user/domain/interface.dart'; +import 'package:dartz/dartz.dart'; +import 'package:flowy_sdk/protobuf/errors.pb.dart'; +import 'package:flowy_sdk/protobuf/user_detail.pb.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; +// ignore: import_of_legacy_library_into_null_safe +import 'package:flutter_bloc/flutter_bloc.dart'; + +part 'sign_in_event.dart'; +part 'sign_in_state.dart'; +part 'sign_in_bloc.freezed.dart'; + +class SignInBloc extends Bloc { + final IAuth authImpl; + SignInBloc(this.authImpl) : super(SignInState.initial()); + + @override + Stream mapEventToState( + SignInEvent event, + ) async* { + yield* event.map( + signedInWithUserEmailAndPassword: (e) async* { + yield* _performActionOnSignIn( + state, + ); + }, + emailChanged: (EmailChanged value) async* { + yield state.copyWith(email: value.email, signInFailure: none()); + }, + passwordChanged: (PasswordChanged value) async* { + yield state.copyWith(password: value.password, signInFailure: none()); + }, + ); + } + + Stream _performActionOnSignIn(SignInState state) async* { + yield state.copyWith(isSubmitting: true); + + final result = await authImpl.signIn(state.email, state.password); + yield result.fold( + (userDetail) => state.copyWith( + isSubmitting: false, signInFailure: some(left(userDetail))), + (s) => state.copyWith(isSubmitting: false, signInFailure: some(right(s))), + ); + } +} diff --git a/app_flowy/lib/user/application/sign_in/sign_in_bloc.freezed.dart b/app_flowy/lib/user/application/sign_in/sign_in_bloc.freezed.dart new file mode 100644 index 0000000000..331edfc453 --- /dev/null +++ b/app_flowy/lib/user/application/sign_in/sign_in_bloc.freezed.dart @@ -0,0 +1,642 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND +// 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 + +part of 'sign_in_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 informations: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); + +/// @nodoc +class _$SignInEventTearOff { + const _$SignInEventTearOff(); + + SignedInWithUserEmailAndPassword signedInWithUserEmailAndPassword() { + return const SignedInWithUserEmailAndPassword(); + } + + EmailChanged emailChanged(String email) { + return EmailChanged( + email, + ); + } + + PasswordChanged passwordChanged(String password) { + return PasswordChanged( + password, + ); + } +} + +/// @nodoc +const $SignInEvent = _$SignInEventTearOff(); + +/// @nodoc +mixin _$SignInEvent { + @optionalTypeArgs + TResult when({ + required TResult Function() signedInWithUserEmailAndPassword, + required TResult Function(String email) emailChanged, + required TResult Function(String password) passwordChanged, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? signedInWithUserEmailAndPassword, + TResult Function(String email)? emailChanged, + TResult Function(String password)? passwordChanged, + required TResult orElse(), + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult map({ + required TResult Function(SignedInWithUserEmailAndPassword value) + signedInWithUserEmailAndPassword, + required TResult Function(EmailChanged value) emailChanged, + required TResult Function(PasswordChanged value) passwordChanged, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult maybeMap({ + TResult Function(SignedInWithUserEmailAndPassword value)? + signedInWithUserEmailAndPassword, + TResult Function(EmailChanged value)? emailChanged, + TResult Function(PasswordChanged value)? passwordChanged, + required TResult orElse(), + }) => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $SignInEventCopyWith<$Res> { + factory $SignInEventCopyWith( + SignInEvent value, $Res Function(SignInEvent) then) = + _$SignInEventCopyWithImpl<$Res>; +} + +/// @nodoc +class _$SignInEventCopyWithImpl<$Res> implements $SignInEventCopyWith<$Res> { + _$SignInEventCopyWithImpl(this._value, this._then); + + final SignInEvent _value; + // ignore: unused_field + final $Res Function(SignInEvent) _then; +} + +/// @nodoc +abstract class $SignedInWithUserEmailAndPasswordCopyWith<$Res> { + factory $SignedInWithUserEmailAndPasswordCopyWith( + SignedInWithUserEmailAndPassword value, + $Res Function(SignedInWithUserEmailAndPassword) then) = + _$SignedInWithUserEmailAndPasswordCopyWithImpl<$Res>; +} + +/// @nodoc +class _$SignedInWithUserEmailAndPasswordCopyWithImpl<$Res> + extends _$SignInEventCopyWithImpl<$Res> + implements $SignedInWithUserEmailAndPasswordCopyWith<$Res> { + _$SignedInWithUserEmailAndPasswordCopyWithImpl( + SignedInWithUserEmailAndPassword _value, + $Res Function(SignedInWithUserEmailAndPassword) _then) + : super(_value, (v) => _then(v as SignedInWithUserEmailAndPassword)); + + @override + SignedInWithUserEmailAndPassword get _value => + super._value as SignedInWithUserEmailAndPassword; +} + +/// @nodoc + +class _$SignedInWithUserEmailAndPassword + implements SignedInWithUserEmailAndPassword { + const _$SignedInWithUserEmailAndPassword(); + + @override + String toString() { + return 'SignInEvent.signedInWithUserEmailAndPassword()'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other is SignedInWithUserEmailAndPassword); + } + + @override + int get hashCode => runtimeType.hashCode; + + @override + @optionalTypeArgs + TResult when({ + required TResult Function() signedInWithUserEmailAndPassword, + required TResult Function(String email) emailChanged, + required TResult Function(String password) passwordChanged, + }) { + return signedInWithUserEmailAndPassword(); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? signedInWithUserEmailAndPassword, + TResult Function(String email)? emailChanged, + TResult Function(String password)? passwordChanged, + required TResult orElse(), + }) { + if (signedInWithUserEmailAndPassword != null) { + return signedInWithUserEmailAndPassword(); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(SignedInWithUserEmailAndPassword value) + signedInWithUserEmailAndPassword, + required TResult Function(EmailChanged value) emailChanged, + required TResult Function(PasswordChanged value) passwordChanged, + }) { + return signedInWithUserEmailAndPassword(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(SignedInWithUserEmailAndPassword value)? + signedInWithUserEmailAndPassword, + TResult Function(EmailChanged value)? emailChanged, + TResult Function(PasswordChanged value)? passwordChanged, + required TResult orElse(), + }) { + if (signedInWithUserEmailAndPassword != null) { + return signedInWithUserEmailAndPassword(this); + } + return orElse(); + } +} + +abstract class SignedInWithUserEmailAndPassword implements SignInEvent { + const factory SignedInWithUserEmailAndPassword() = + _$SignedInWithUserEmailAndPassword; +} + +/// @nodoc +abstract class $EmailChangedCopyWith<$Res> { + factory $EmailChangedCopyWith( + EmailChanged value, $Res Function(EmailChanged) then) = + _$EmailChangedCopyWithImpl<$Res>; + $Res call({String email}); +} + +/// @nodoc +class _$EmailChangedCopyWithImpl<$Res> extends _$SignInEventCopyWithImpl<$Res> + implements $EmailChangedCopyWith<$Res> { + _$EmailChangedCopyWithImpl( + EmailChanged _value, $Res Function(EmailChanged) _then) + : super(_value, (v) => _then(v as EmailChanged)); + + @override + EmailChanged get _value => super._value as EmailChanged; + + @override + $Res call({ + Object? email = freezed, + }) { + return _then(EmailChanged( + email == freezed + ? _value.email + : email // ignore: cast_nullable_to_non_nullable + as String, + )); + } +} + +/// @nodoc + +class _$EmailChanged implements EmailChanged { + const _$EmailChanged(this.email); + + @override + final String email; + + @override + String toString() { + return 'SignInEvent.emailChanged(email: $email)'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other is EmailChanged && + (identical(other.email, email) || + const DeepCollectionEquality().equals(other.email, email))); + } + + @override + int get hashCode => + runtimeType.hashCode ^ const DeepCollectionEquality().hash(email); + + @JsonKey(ignore: true) + @override + $EmailChangedCopyWith get copyWith => + _$EmailChangedCopyWithImpl(this, _$identity); + + @override + @optionalTypeArgs + TResult when({ + required TResult Function() signedInWithUserEmailAndPassword, + required TResult Function(String email) emailChanged, + required TResult Function(String password) passwordChanged, + }) { + return emailChanged(email); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? signedInWithUserEmailAndPassword, + TResult Function(String email)? emailChanged, + TResult Function(String password)? passwordChanged, + required TResult orElse(), + }) { + if (emailChanged != null) { + return emailChanged(email); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(SignedInWithUserEmailAndPassword value) + signedInWithUserEmailAndPassword, + required TResult Function(EmailChanged value) emailChanged, + required TResult Function(PasswordChanged value) passwordChanged, + }) { + return emailChanged(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(SignedInWithUserEmailAndPassword value)? + signedInWithUserEmailAndPassword, + TResult Function(EmailChanged value)? emailChanged, + TResult Function(PasswordChanged value)? passwordChanged, + required TResult orElse(), + }) { + if (emailChanged != null) { + return emailChanged(this); + } + return orElse(); + } +} + +abstract class EmailChanged implements SignInEvent { + const factory EmailChanged(String email) = _$EmailChanged; + + String get email => throw _privateConstructorUsedError; + @JsonKey(ignore: true) + $EmailChangedCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $PasswordChangedCopyWith<$Res> { + factory $PasswordChangedCopyWith( + PasswordChanged value, $Res Function(PasswordChanged) then) = + _$PasswordChangedCopyWithImpl<$Res>; + $Res call({String password}); +} + +/// @nodoc +class _$PasswordChangedCopyWithImpl<$Res> + extends _$SignInEventCopyWithImpl<$Res> + implements $PasswordChangedCopyWith<$Res> { + _$PasswordChangedCopyWithImpl( + PasswordChanged _value, $Res Function(PasswordChanged) _then) + : super(_value, (v) => _then(v as PasswordChanged)); + + @override + PasswordChanged get _value => super._value as PasswordChanged; + + @override + $Res call({ + Object? password = freezed, + }) { + return _then(PasswordChanged( + password == freezed + ? _value.password + : password // ignore: cast_nullable_to_non_nullable + as String, + )); + } +} + +/// @nodoc + +class _$PasswordChanged implements PasswordChanged { + const _$PasswordChanged(this.password); + + @override + final String password; + + @override + String toString() { + return 'SignInEvent.passwordChanged(password: $password)'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other is PasswordChanged && + (identical(other.password, password) || + const DeepCollectionEquality() + .equals(other.password, password))); + } + + @override + int get hashCode => + runtimeType.hashCode ^ const DeepCollectionEquality().hash(password); + + @JsonKey(ignore: true) + @override + $PasswordChangedCopyWith get copyWith => + _$PasswordChangedCopyWithImpl(this, _$identity); + + @override + @optionalTypeArgs + TResult when({ + required TResult Function() signedInWithUserEmailAndPassword, + required TResult Function(String email) emailChanged, + required TResult Function(String password) passwordChanged, + }) { + return passwordChanged(password); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? signedInWithUserEmailAndPassword, + TResult Function(String email)? emailChanged, + TResult Function(String password)? passwordChanged, + required TResult orElse(), + }) { + if (passwordChanged != null) { + return passwordChanged(password); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(SignedInWithUserEmailAndPassword value) + signedInWithUserEmailAndPassword, + required TResult Function(EmailChanged value) emailChanged, + required TResult Function(PasswordChanged value) passwordChanged, + }) { + return passwordChanged(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(SignedInWithUserEmailAndPassword value)? + signedInWithUserEmailAndPassword, + TResult Function(EmailChanged value)? emailChanged, + TResult Function(PasswordChanged value)? passwordChanged, + required TResult orElse(), + }) { + if (passwordChanged != null) { + return passwordChanged(this); + } + return orElse(); + } +} + +abstract class PasswordChanged implements SignInEvent { + const factory PasswordChanged(String password) = _$PasswordChanged; + + String get password => throw _privateConstructorUsedError; + @JsonKey(ignore: true) + $PasswordChangedCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +class _$SignInStateTearOff { + const _$SignInStateTearOff(); + + _SignInState call( + {String? email, + String? password, + required bool isSubmitting, + required Option> signInFailure}) { + return _SignInState( + email: email, + password: password, + isSubmitting: isSubmitting, + signInFailure: signInFailure, + ); + } +} + +/// @nodoc +const $SignInState = _$SignInStateTearOff(); + +/// @nodoc +mixin _$SignInState { + String? get email => throw _privateConstructorUsedError; + String? get password => throw _privateConstructorUsedError; + bool get isSubmitting => throw _privateConstructorUsedError; + Option> get signInFailure => + throw _privateConstructorUsedError; + + @JsonKey(ignore: true) + $SignInStateCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $SignInStateCopyWith<$Res> { + factory $SignInStateCopyWith( + SignInState value, $Res Function(SignInState) then) = + _$SignInStateCopyWithImpl<$Res>; + $Res call( + {String? email, + String? password, + bool isSubmitting, + Option> signInFailure}); +} + +/// @nodoc +class _$SignInStateCopyWithImpl<$Res> implements $SignInStateCopyWith<$Res> { + _$SignInStateCopyWithImpl(this._value, this._then); + + final SignInState _value; + // ignore: unused_field + final $Res Function(SignInState) _then; + + @override + $Res call({ + Object? email = freezed, + Object? password = freezed, + Object? isSubmitting = freezed, + Object? signInFailure = freezed, + }) { + return _then(_value.copyWith( + email: email == freezed + ? _value.email + : email // ignore: cast_nullable_to_non_nullable + as String?, + password: password == freezed + ? _value.password + : password // ignore: cast_nullable_to_non_nullable + as String?, + isSubmitting: isSubmitting == freezed + ? _value.isSubmitting + : isSubmitting // ignore: cast_nullable_to_non_nullable + as bool, + signInFailure: signInFailure == freezed + ? _value.signInFailure + : signInFailure // ignore: cast_nullable_to_non_nullable + as Option>, + )); + } +} + +/// @nodoc +abstract class _$SignInStateCopyWith<$Res> + implements $SignInStateCopyWith<$Res> { + factory _$SignInStateCopyWith( + _SignInState value, $Res Function(_SignInState) then) = + __$SignInStateCopyWithImpl<$Res>; + @override + $Res call( + {String? email, + String? password, + bool isSubmitting, + Option> signInFailure}); +} + +/// @nodoc +class __$SignInStateCopyWithImpl<$Res> extends _$SignInStateCopyWithImpl<$Res> + implements _$SignInStateCopyWith<$Res> { + __$SignInStateCopyWithImpl( + _SignInState _value, $Res Function(_SignInState) _then) + : super(_value, (v) => _then(v as _SignInState)); + + @override + _SignInState get _value => super._value as _SignInState; + + @override + $Res call({ + Object? email = freezed, + Object? password = freezed, + Object? isSubmitting = freezed, + Object? signInFailure = freezed, + }) { + return _then(_SignInState( + email: email == freezed + ? _value.email + : email // ignore: cast_nullable_to_non_nullable + as String?, + password: password == freezed + ? _value.password + : password // ignore: cast_nullable_to_non_nullable + as String?, + isSubmitting: isSubmitting == freezed + ? _value.isSubmitting + : isSubmitting // ignore: cast_nullable_to_non_nullable + as bool, + signInFailure: signInFailure == freezed + ? _value.signInFailure + : signInFailure // ignore: cast_nullable_to_non_nullable + as Option>, + )); + } +} + +/// @nodoc + +class _$_SignInState implements _SignInState { + const _$_SignInState( + {this.email, + this.password, + required this.isSubmitting, + required this.signInFailure}); + + @override + final String? email; + @override + final String? password; + @override + final bool isSubmitting; + @override + final Option> signInFailure; + + @override + String toString() { + return 'SignInState(email: $email, password: $password, isSubmitting: $isSubmitting, signInFailure: $signInFailure)'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other is _SignInState && + (identical(other.email, email) || + const DeepCollectionEquality().equals(other.email, email)) && + (identical(other.password, password) || + const DeepCollectionEquality() + .equals(other.password, password)) && + (identical(other.isSubmitting, isSubmitting) || + const DeepCollectionEquality() + .equals(other.isSubmitting, isSubmitting)) && + (identical(other.signInFailure, signInFailure) || + const DeepCollectionEquality() + .equals(other.signInFailure, signInFailure))); + } + + @override + int get hashCode => + runtimeType.hashCode ^ + const DeepCollectionEquality().hash(email) ^ + const DeepCollectionEquality().hash(password) ^ + const DeepCollectionEquality().hash(isSubmitting) ^ + const DeepCollectionEquality().hash(signInFailure); + + @JsonKey(ignore: true) + @override + _$SignInStateCopyWith<_SignInState> get copyWith => + __$SignInStateCopyWithImpl<_SignInState>(this, _$identity); +} + +abstract class _SignInState implements SignInState { + const factory _SignInState( + {String? email, + String? password, + required bool isSubmitting, + required Option> signInFailure}) = + _$_SignInState; + + @override + String? get email => throw _privateConstructorUsedError; + @override + String? get password => throw _privateConstructorUsedError; + @override + bool get isSubmitting => throw _privateConstructorUsedError; + @override + Option> get signInFailure => + throw _privateConstructorUsedError; + @override + @JsonKey(ignore: true) + _$SignInStateCopyWith<_SignInState> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/app_flowy/lib/user/application/sign_in/sign_in_event.dart b/app_flowy/lib/user/application/sign_in/sign_in_event.dart index e69de29bb2..702afea0bc 100644 --- a/app_flowy/lib/user/application/sign_in/sign_in_event.dart +++ b/app_flowy/lib/user/application/sign_in/sign_in_event.dart @@ -0,0 +1,10 @@ +part of 'sign_in_bloc.dart'; + +@freezed +abstract class SignInEvent with _$SignInEvent { + const factory SignInEvent.signedInWithUserEmailAndPassword() = + SignedInWithUserEmailAndPassword; + + const factory SignInEvent.emailChanged(String email) = EmailChanged; + const factory SignInEvent.passwordChanged(String password) = PasswordChanged; +} diff --git a/app_flowy/lib/user/application/sign_in/sign_in_state.dart b/app_flowy/lib/user/application/sign_in/sign_in_state.dart index e69de29bb2..76555cb38b 100644 --- a/app_flowy/lib/user/application/sign_in/sign_in_state.dart +++ b/app_flowy/lib/user/application/sign_in/sign_in_state.dart @@ -0,0 +1,16 @@ +part of 'sign_in_bloc.dart'; + +@freezed +abstract class SignInState with _$SignInState { + const factory SignInState({ + String? email, + String? password, + required bool isSubmitting, + required Option> signInFailure, + }) = _SignInState; + + factory SignInState.initial() => SignInState( + isSubmitting: false, + signInFailure: none(), + ); +} diff --git a/app_flowy/lib/user/domain/interface.dart b/app_flowy/lib/user/domain/interface.dart new file mode 100644 index 0000000000..c5d82a3e2b --- /dev/null +++ b/app_flowy/lib/user/domain/interface.dart @@ -0,0 +1,9 @@ +import 'package:flowy_sdk/protobuf/errors.pb.dart'; +import 'package:flowy_sdk/protobuf/user_detail.pb.dart'; +import 'package:dartz/dartz.dart'; + +abstract class IAuth { + Future> signIn(String? email, String? password); + Future> signUp( + String? name, String? password, String? email); +} diff --git a/app_flowy/lib/user/infrastructure/auth_repo.dart b/app_flowy/lib/user/infrastructure/auth_repo.dart new file mode 100644 index 0000000000..b468c945d6 --- /dev/null +++ b/app_flowy/lib/user/infrastructure/auth_repo.dart @@ -0,0 +1,30 @@ +import 'package:dartz/dartz.dart'; +import 'package:flowy_sdk/dispatch/dispatch.dart'; +import 'package:flowy_sdk/protobuf/errors.pb.dart'; +import 'package:flowy_sdk/protobuf/sign_in.pb.dart'; +import 'package:flowy_sdk/protobuf/sign_up.pb.dart'; +import 'package:flowy_sdk/protobuf/user_detail.pb.dart'; + +class AuthRepository { + Future> signIn( + {required String? email, required String? password}) { + // + final request = SignInRequest.create() + ..email = email ?? '' + ..password = password ?? ''; + + return UserEventSignIn(request).send(); + } + + Future> signUp( + {required String? name, + required String? password, + required String? email}) { + final request = SignUpRequest.create() + ..email = email ?? '' + ..name = name ?? '' + ..password = password ?? ''; + + return UserEventSignUp(request).send(); + } +} diff --git a/app_flowy/lib/user/infrastructure/interface_impl.dart b/app_flowy/lib/user/infrastructure/interface_impl.dart new file mode 100644 index 0000000000..bfe7b1cb63 --- /dev/null +++ b/app_flowy/lib/user/infrastructure/interface_impl.dart @@ -0,0 +1,39 @@ +import 'package:app_flowy/user/application/sign_in/sign_in_bloc.dart'; +import 'package:dartz/dartz.dart'; +import 'package:flowy_sdk/protobuf/errors.pb.dart'; +import 'package:flowy_sdk/protobuf/user_detail.pb.dart'; +import 'package:get_it/get_it.dart'; + +import 'package:app_flowy/user/domain/interface.dart'; +import 'package:app_flowy/user/infrastructure/auth_repo.dart'; + +class UserDepsResolver { + static Future resolve(GetIt getIt) async { + getIt.registerLazySingleton(() => AuthRepository()); + + //Interface implementation + getIt.registerFactory(() => AuthImpl(repo: getIt())); + + //Bloc + getIt.registerFactory(() => SignInBloc(getIt())); + } +} + +class AuthImpl extends IAuth { + AuthRepository repo; + AuthImpl({ + required this.repo, + }); + + @override + Future> signIn( + String? email, String? password) { + return repo.signIn(email: email, password: password); + } + + @override + Future> signUp( + String? name, String? password, String? email) { + return repo.signUp(name: name, password: password, email: email); + } +} diff --git a/app_flowy/lib/user/presentation/sign_in/sign_in_screen.dart b/app_flowy/lib/user/presentation/sign_in/sign_in_screen.dart index e69de29bb2..286463167c 100644 --- a/app_flowy/lib/user/presentation/sign_in/sign_in_screen.dart +++ b/app_flowy/lib/user/presentation/sign_in/sign_in_screen.dart @@ -0,0 +1,19 @@ +import 'package:app_flowy/startup/startup.dart'; +import 'package:app_flowy/user/application/sign_in/sign_in_bloc.dart'; +import 'package:app_flowy/user/presentation/sign_in/widgets/body.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +class SignInScreen extends StatelessWidget { + const SignInScreen({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return BlocProvider( + create: (context) => getIt(), + child: const Scaffold( + body: Body(), + ), + ); + } +} diff --git a/app_flowy/lib/user/presentation/sign_in/widgets/background.dart b/app_flowy/lib/user/presentation/sign_in/widgets/background.dart new file mode 100644 index 0000000000..a33ae64e27 --- /dev/null +++ b/app_flowy/lib/user/presentation/sign_in/widgets/background.dart @@ -0,0 +1,29 @@ +import 'package:flutter/material.dart'; + +class SignInBackground extends StatelessWidget { + final Widget child; + const SignInBackground({ + Key? key, + required this.child, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + var size = MediaQuery.of(context).size; + return SizedBox( + height: size.height, + width: double.infinity, + child: Stack( + alignment: Alignment.center, + children: [ + Image( + fit: BoxFit.cover, + width: size.width, + height: size.height, + image: const AssetImage( + 'assets/images/appflowy_launch_splash.jpg')), + child, + ], + )); + } +} diff --git a/app_flowy/lib/user/presentation/sign_in/widgets/body.dart b/app_flowy/lib/user/presentation/sign_in/widgets/body.dart new file mode 100644 index 0000000000..c7fa470338 --- /dev/null +++ b/app_flowy/lib/user/presentation/sign_in/widgets/body.dart @@ -0,0 +1,126 @@ +import 'package:app_flowy/home/presentation/home_screen.dart'; +import 'package:app_flowy/startup/startup.dart'; +import 'package:app_flowy/user/application/sign_in/sign_in_bloc.dart'; +import 'package:app_flowy/user/presentation/sign_in/widgets/background.dart'; +import 'package:dartz/dartz.dart'; +import 'package:flowy_infra_ui/widget/rounded_button.dart'; +import 'package:flowy_infra_ui/widget/rounded_input_field.dart'; +import 'package:flowy_sdk/protobuf/errors.pb.dart'; +import 'package:flowy_sdk/protobuf/user_detail.pb.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +class Body extends StatelessWidget { + const Body({Key? key}) : super(key: key); + @override + Widget build(BuildContext context) { + return BlocProvider( + create: (context) => getIt(), + child: const SignInBackground( + child: SignInForm(), + ), + ); + } +} + +class SignInForm extends StatelessWidget { + const SignInForm({ + Key? key, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return BlocConsumer( + listenWhen: (p, c) => p != c, + listener: (context, state) { + state.signInFailure.fold( + () {}, + (result) => _handleStateErrors(result, context), + ); + }, + builder: (context, state) { + return SignInFormBackground( + children: [ + const SizedBox(height: 30), + RoundedInputField( + icon: Icons.person, + hintText: 'email', + onChanged: (value) => context + .read() + .add(SignInEvent.emailChanged(value)), + ), + RoundedInputField( + icon: Icons.lock, + obscureText: true, + hintText: 'password', + onChanged: (value) => context + .read() + .add(SignInEvent.passwordChanged(value)), + ), + RoundedButton( + title: 'LOGIN', + press: () { + context + .read() + .add(const SignInEvent.signedInWithUserEmailAndPassword()); + }, + ), + if (state.isSubmitting) ...[ + const SizedBox(height: 8), + const LinearProgressIndicator(value: null), + ] + ], + ); + }, + ); + } + + void _handleStateErrors( + Either some, BuildContext context) { + some.fold( + (userDetail) => showHomeScreen(context, userDetail), + (result) => _showErrorMessage(context, result.msg), + ); + } + + void _showErrorMessage(BuildContext context, String msg) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(msg), + ), + ); + } + + void showHomeScreen(BuildContext context, UserDetail userDetail) { + Navigator.pushReplacement( + context, + MaterialPageRoute( + builder: (context) { + return HomeScreen(userDetail); + }, + ), + ); + } +} + +class SignInFormBackground extends StatelessWidget { + final List children; + const SignInFormBackground({ + Key? key, + required this.children, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + final size = MediaQuery.of(context).size; + + return Container( + width: size.width * 0.4, + alignment: Alignment.center, + child: SingleChildScrollView( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, children: children), + ), + ); + } +} diff --git a/app_flowy/lib/welcome/infrastructure/interface_impl.dart b/app_flowy/lib/welcome/infrastructure/interface_impl.dart index 1ff8f38aad..ddbb421a6d 100644 --- a/app_flowy/lib/welcome/infrastructure/interface_impl.dart +++ b/app_flowy/lib/welcome/infrastructure/interface_impl.dart @@ -3,6 +3,7 @@ import 'package:app_flowy/home/application/home_bloc.dart'; import 'package:app_flowy/home/application/menu/menu_bloc.dart'; import 'package:app_flowy/home/application/watcher/home_watcher_bloc.dart'; import 'package:app_flowy/home/presentation/home_screen.dart'; +import 'package:app_flowy/user/presentation/sign_in/sign_in_screen.dart'; import 'package:app_flowy/welcome/application/welcome_bloc.dart'; import 'package:app_flowy/welcome/domain/auth_state.dart'; import 'package:app_flowy/welcome/domain/interface.dart'; @@ -12,8 +13,8 @@ import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; import 'package:get_it/get_it.dart'; -class Welcome { - static Future dependencyResolved(GetIt getIt) async { +class WelcomeDepsResolver { + static Future resolve(GetIt getIt) async { getIt.registerFactory(() => WelcomeAuthImpl()); getIt.registerFactory(() => WelcomeRoute()); getIt.registerFactory(() => HomeBloc()); @@ -52,10 +53,6 @@ class WelcomeRoute implements IWelcomeRoute { @override Widget pushSignInScreen() { - return Container( - width: 100, - height: 100, - color: Colors.red, - ); + return const SignInScreen(); } } diff --git a/rust-lib/flowy-sdk/src/lib.rs b/rust-lib/flowy-sdk/src/lib.rs index 9f3cd3c6f2..494d220d8b 100644 --- a/rust-lib/flowy-sdk/src/lib.rs +++ b/rust-lib/flowy-sdk/src/lib.rs @@ -3,21 +3,25 @@ pub use module::*; use flowy_dispatch::prelude::*; use module::build_modules; +use std::sync::atomic::{AtomicBool, Ordering}; +static INIT_LOG: AtomicBool = AtomicBool::new(false); pub struct FlowySDK {} impl FlowySDK { - pub fn init_log(directory: &str) { flowy_log::init_log("flowy", directory, "Debug").unwrap(); } + pub fn init_log(directory: &str) { + if !INIT_LOG.load(Ordering::SeqCst) { + INIT_LOG.store(true, Ordering::SeqCst); + flowy_log::init_log("flowy", directory, "Debug").unwrap(); + } + } pub fn init(path: &str) { tracing::info!("🔥 Root path: {}", path); - flowy_infra::kv::KVStore::init(path); - let config = ModuleConfig { root: path.to_string(), }; - EventDispatch::construct(|| build_modules(config)); } }