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));
}
}