diff --git a/app_flowy/assets/images/small_logo.svg b/app_flowy/assets/images/flowy_logo.svg similarity index 100% rename from app_flowy/assets/images/small_logo.svg rename to app_flowy/assets/images/flowy_logo.svg diff --git a/app_flowy/lib/user/application/sign_in/sign_in_bloc.dart b/app_flowy/lib/user/application/sign_in_bloc.dart similarity index 94% rename from app_flowy/lib/user/application/sign_in/sign_in_bloc.dart rename to app_flowy/lib/user/application/sign_in_bloc.dart index 0bd9f1660e..1cd6cec773 100644 --- a/app_flowy/lib/user/application/sign_in/sign_in_bloc.dart +++ b/app_flowy/lib/user/application/sign_in_bloc.dart @@ -2,7 +2,6 @@ import 'package:app_flowy/user/domain/i_auth.dart'; import 'package:dartz/dartz.dart'; import 'package:flowy_sdk/protobuf/flowy-user/protobuf.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_bloc.freezed.dart'; @@ -35,8 +34,8 @@ class SignInBloc extends Bloc { final result = await authImpl.signIn(state.email, state.password); yield result.fold( - (UserProfile) => state.copyWith( - isSubmitting: false, successOrFail: some(left(UserProfile))), + (userProfile) => state.copyWith( + isSubmitting: false, successOrFail: some(left(userProfile))), (error) => stateFromCode(error), ); } diff --git a/app_flowy/lib/user/application/sign_in/sign_in_bloc.freezed.dart b/app_flowy/lib/user/application/sign_in_bloc.freezed.dart similarity index 100% rename from app_flowy/lib/user/application/sign_in/sign_in_bloc.freezed.dart rename to app_flowy/lib/user/application/sign_in_bloc.freezed.dart diff --git a/app_flowy/lib/user/application/sign_up/sign_up_bloc.dart b/app_flowy/lib/user/application/sign_up/sign_up_bloc.dart deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/app_flowy/lib/user/application/sign_up_bloc.dart b/app_flowy/lib/user/application/sign_up_bloc.dart new file mode 100644 index 0000000000..bc408ac48e --- /dev/null +++ b/app_flowy/lib/user/application/sign_up_bloc.dart @@ -0,0 +1,87 @@ +import 'package:app_flowy/user/domain/i_auth.dart'; +import 'package:dartz/dartz.dart'; +import 'package:flowy_sdk/protobuf/flowy-user/protobuf.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +part 'sign_up_bloc.freezed.dart'; + +class SignUpBloc extends Bloc { + final IAuth authImpl; + SignUpBloc(this.authImpl) : super(SignUpState.initial()); + + @override + Stream mapEventToState( + SignUpEvent event, + ) async* { + yield* event.map( + signUpWithUserEmailAndPassword: (e) async* { + yield* _performActionOnSignUp( + state, + ); + }, + emailChanged: (EmailChanged value) async* { + yield state.copyWith(email: value.email, successOrFail: none()); + }, + passwordChanged: (PasswordChanged value) async* { + yield state.copyWith(password: value.password, successOrFail: none()); + }, + ); + } + + Stream _performActionOnSignUp(SignUpState state) async* { + yield state.copyWith(isSubmitting: true); + + final result = await authImpl.signIn(state.email, state.password); + yield result.fold( + (userProfile) => state.copyWith( + isSubmitting: false, successOrFail: some(left(userProfile))), + (error) => stateFromCode(error), + ); + } + + SignUpState stateFromCode(UserError error) { + switch (error.code) { + case ErrorCode.EmailFormatInvalid: + return state.copyWith( + isSubmitting: false, + emailError: some(error.msg), + passwordError: none()); + case ErrorCode.PasswordFormatInvalid: + return state.copyWith( + isSubmitting: false, + passwordError: some(error.msg), + emailError: none()); + default: + return state.copyWith( + isSubmitting: false, successOrFail: some(right(error))); + } + } +} + +@freezed +abstract class SignUpEvent with _$SignUpEvent { + const factory SignUpEvent.signUpWithUserEmailAndPassword() = + SignUpWithUserEmailAndPassword; + const factory SignUpEvent.emailChanged(String email) = EmailChanged; + const factory SignUpEvent.passwordChanged(String password) = PasswordChanged; +} + +@freezed +abstract class SignUpState with _$SignUpState { + const factory SignUpState({ + String? email, + String? password, + required bool isSubmitting, + required Option passwordError, + required Option emailError, + required Option> successOrFail, + }) = _SignUpState; + + factory SignUpState.initial() => SignUpState( + isSubmitting: false, + passwordError: none(), + emailError: none(), + successOrFail: none(), + ); +} diff --git a/app_flowy/lib/user/application/sign_up_bloc.freezed.dart b/app_flowy/lib/user/application/sign_up_bloc.freezed.dart new file mode 100644 index 0000000000..c04acf8a74 --- /dev/null +++ b/app_flowy/lib/user/application/sign_up_bloc.freezed.dart @@ -0,0 +1,691 @@ +// 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, invalid_annotation_target + +part of 'sign_up_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 _$SignUpEventTearOff { + const _$SignUpEventTearOff(); + + SignUpWithUserEmailAndPassword signUpWithUserEmailAndPassword() { + return const SignUpWithUserEmailAndPassword(); + } + + EmailChanged emailChanged(String email) { + return EmailChanged( + email, + ); + } + + PasswordChanged passwordChanged(String password) { + return PasswordChanged( + password, + ); + } +} + +/// @nodoc +const $SignUpEvent = _$SignUpEventTearOff(); + +/// @nodoc +mixin _$SignUpEvent { + @optionalTypeArgs + TResult when({ + required TResult Function() signUpWithUserEmailAndPassword, + required TResult Function(String email) emailChanged, + required TResult Function(String password) passwordChanged, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? signUpWithUserEmailAndPassword, + TResult Function(String email)? emailChanged, + TResult Function(String password)? passwordChanged, + required TResult orElse(), + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult map({ + required TResult Function(SignUpWithUserEmailAndPassword value) + signUpWithUserEmailAndPassword, + required TResult Function(EmailChanged value) emailChanged, + required TResult Function(PasswordChanged value) passwordChanged, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult maybeMap({ + TResult Function(SignUpWithUserEmailAndPassword value)? + signUpWithUserEmailAndPassword, + TResult Function(EmailChanged value)? emailChanged, + TResult Function(PasswordChanged value)? passwordChanged, + required TResult orElse(), + }) => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $SignUpEventCopyWith<$Res> { + factory $SignUpEventCopyWith( + SignUpEvent value, $Res Function(SignUpEvent) then) = + _$SignUpEventCopyWithImpl<$Res>; +} + +/// @nodoc +class _$SignUpEventCopyWithImpl<$Res> implements $SignUpEventCopyWith<$Res> { + _$SignUpEventCopyWithImpl(this._value, this._then); + + final SignUpEvent _value; + // ignore: unused_field + final $Res Function(SignUpEvent) _then; +} + +/// @nodoc +abstract class $SignUpWithUserEmailAndPasswordCopyWith<$Res> { + factory $SignUpWithUserEmailAndPasswordCopyWith( + SignUpWithUserEmailAndPassword value, + $Res Function(SignUpWithUserEmailAndPassword) then) = + _$SignUpWithUserEmailAndPasswordCopyWithImpl<$Res>; +} + +/// @nodoc +class _$SignUpWithUserEmailAndPasswordCopyWithImpl<$Res> + extends _$SignUpEventCopyWithImpl<$Res> + implements $SignUpWithUserEmailAndPasswordCopyWith<$Res> { + _$SignUpWithUserEmailAndPasswordCopyWithImpl( + SignUpWithUserEmailAndPassword _value, + $Res Function(SignUpWithUserEmailAndPassword) _then) + : super(_value, (v) => _then(v as SignUpWithUserEmailAndPassword)); + + @override + SignUpWithUserEmailAndPassword get _value => + super._value as SignUpWithUserEmailAndPassword; +} + +/// @nodoc + +class _$SignUpWithUserEmailAndPassword + implements SignUpWithUserEmailAndPassword { + const _$SignUpWithUserEmailAndPassword(); + + @override + String toString() { + return 'SignUpEvent.signUpWithUserEmailAndPassword()'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || (other is SignUpWithUserEmailAndPassword); + } + + @override + int get hashCode => runtimeType.hashCode; + + @override + @optionalTypeArgs + TResult when({ + required TResult Function() signUpWithUserEmailAndPassword, + required TResult Function(String email) emailChanged, + required TResult Function(String password) passwordChanged, + }) { + return signUpWithUserEmailAndPassword(); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? signUpWithUserEmailAndPassword, + TResult Function(String email)? emailChanged, + TResult Function(String password)? passwordChanged, + required TResult orElse(), + }) { + if (signUpWithUserEmailAndPassword != null) { + return signUpWithUserEmailAndPassword(); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(SignUpWithUserEmailAndPassword value) + signUpWithUserEmailAndPassword, + required TResult Function(EmailChanged value) emailChanged, + required TResult Function(PasswordChanged value) passwordChanged, + }) { + return signUpWithUserEmailAndPassword(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(SignUpWithUserEmailAndPassword value)? + signUpWithUserEmailAndPassword, + TResult Function(EmailChanged value)? emailChanged, + TResult Function(PasswordChanged value)? passwordChanged, + required TResult orElse(), + }) { + if (signUpWithUserEmailAndPassword != null) { + return signUpWithUserEmailAndPassword(this); + } + return orElse(); + } +} + +abstract class SignUpWithUserEmailAndPassword implements SignUpEvent { + const factory SignUpWithUserEmailAndPassword() = + _$SignUpWithUserEmailAndPassword; +} + +/// @nodoc +abstract class $EmailChangedCopyWith<$Res> { + factory $EmailChangedCopyWith( + EmailChanged value, $Res Function(EmailChanged) then) = + _$EmailChangedCopyWithImpl<$Res>; + $Res call({String email}); +} + +/// @nodoc +class _$EmailChangedCopyWithImpl<$Res> extends _$SignUpEventCopyWithImpl<$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 'SignUpEvent.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() signUpWithUserEmailAndPassword, + required TResult Function(String email) emailChanged, + required TResult Function(String password) passwordChanged, + }) { + return emailChanged(email); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? signUpWithUserEmailAndPassword, + 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(SignUpWithUserEmailAndPassword value) + signUpWithUserEmailAndPassword, + required TResult Function(EmailChanged value) emailChanged, + required TResult Function(PasswordChanged value) passwordChanged, + }) { + return emailChanged(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(SignUpWithUserEmailAndPassword value)? + signUpWithUserEmailAndPassword, + 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 SignUpEvent { + 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 _$SignUpEventCopyWithImpl<$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 'SignUpEvent.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() signUpWithUserEmailAndPassword, + required TResult Function(String email) emailChanged, + required TResult Function(String password) passwordChanged, + }) { + return passwordChanged(password); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? signUpWithUserEmailAndPassword, + 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(SignUpWithUserEmailAndPassword value) + signUpWithUserEmailAndPassword, + required TResult Function(EmailChanged value) emailChanged, + required TResult Function(PasswordChanged value) passwordChanged, + }) { + return passwordChanged(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(SignUpWithUserEmailAndPassword value)? + signUpWithUserEmailAndPassword, + 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 SignUpEvent { + const factory PasswordChanged(String password) = _$PasswordChanged; + + String get password => throw _privateConstructorUsedError; + @JsonKey(ignore: true) + $PasswordChangedCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +class _$SignUpStateTearOff { + const _$SignUpStateTearOff(); + + _SignUpState call( + {String? email, + String? password, + required bool isSubmitting, + required Option passwordError, + required Option emailError, + required Option> successOrFail}) { + return _SignUpState( + email: email, + password: password, + isSubmitting: isSubmitting, + passwordError: passwordError, + emailError: emailError, + successOrFail: successOrFail, + ); + } +} + +/// @nodoc +const $SignUpState = _$SignUpStateTearOff(); + +/// @nodoc +mixin _$SignUpState { + String? get email => throw _privateConstructorUsedError; + String? get password => throw _privateConstructorUsedError; + bool get isSubmitting => throw _privateConstructorUsedError; + Option get passwordError => throw _privateConstructorUsedError; + Option get emailError => throw _privateConstructorUsedError; + Option> get successOrFail => + throw _privateConstructorUsedError; + + @JsonKey(ignore: true) + $SignUpStateCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $SignUpStateCopyWith<$Res> { + factory $SignUpStateCopyWith( + SignUpState value, $Res Function(SignUpState) then) = + _$SignUpStateCopyWithImpl<$Res>; + $Res call( + {String? email, + String? password, + bool isSubmitting, + Option passwordError, + Option emailError, + Option> successOrFail}); +} + +/// @nodoc +class _$SignUpStateCopyWithImpl<$Res> implements $SignUpStateCopyWith<$Res> { + _$SignUpStateCopyWithImpl(this._value, this._then); + + final SignUpState _value; + // ignore: unused_field + final $Res Function(SignUpState) _then; + + @override + $Res call({ + Object? email = freezed, + Object? password = freezed, + Object? isSubmitting = freezed, + Object? passwordError = freezed, + Object? emailError = freezed, + Object? successOrFail = 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, + passwordError: passwordError == freezed + ? _value.passwordError + : passwordError // ignore: cast_nullable_to_non_nullable + as Option, + emailError: emailError == freezed + ? _value.emailError + : emailError // ignore: cast_nullable_to_non_nullable + as Option, + successOrFail: successOrFail == freezed + ? _value.successOrFail + : successOrFail // ignore: cast_nullable_to_non_nullable + as Option>, + )); + } +} + +/// @nodoc +abstract class _$SignUpStateCopyWith<$Res> + implements $SignUpStateCopyWith<$Res> { + factory _$SignUpStateCopyWith( + _SignUpState value, $Res Function(_SignUpState) then) = + __$SignUpStateCopyWithImpl<$Res>; + @override + $Res call( + {String? email, + String? password, + bool isSubmitting, + Option passwordError, + Option emailError, + Option> successOrFail}); +} + +/// @nodoc +class __$SignUpStateCopyWithImpl<$Res> extends _$SignUpStateCopyWithImpl<$Res> + implements _$SignUpStateCopyWith<$Res> { + __$SignUpStateCopyWithImpl( + _SignUpState _value, $Res Function(_SignUpState) _then) + : super(_value, (v) => _then(v as _SignUpState)); + + @override + _SignUpState get _value => super._value as _SignUpState; + + @override + $Res call({ + Object? email = freezed, + Object? password = freezed, + Object? isSubmitting = freezed, + Object? passwordError = freezed, + Object? emailError = freezed, + Object? successOrFail = freezed, + }) { + return _then(_SignUpState( + 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, + passwordError: passwordError == freezed + ? _value.passwordError + : passwordError // ignore: cast_nullable_to_non_nullable + as Option, + emailError: emailError == freezed + ? _value.emailError + : emailError // ignore: cast_nullable_to_non_nullable + as Option, + successOrFail: successOrFail == freezed + ? _value.successOrFail + : successOrFail // ignore: cast_nullable_to_non_nullable + as Option>, + )); + } +} + +/// @nodoc + +class _$_SignUpState implements _SignUpState { + const _$_SignUpState( + {this.email, + this.password, + required this.isSubmitting, + required this.passwordError, + required this.emailError, + required this.successOrFail}); + + @override + final String? email; + @override + final String? password; + @override + final bool isSubmitting; + @override + final Option passwordError; + @override + final Option emailError; + @override + final Option> successOrFail; + + @override + String toString() { + return 'SignUpState(email: $email, password: $password, isSubmitting: $isSubmitting, passwordError: $passwordError, emailError: $emailError, successOrFail: $successOrFail)'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other is _SignUpState && + (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.passwordError, passwordError) || + const DeepCollectionEquality() + .equals(other.passwordError, passwordError)) && + (identical(other.emailError, emailError) || + const DeepCollectionEquality() + .equals(other.emailError, emailError)) && + (identical(other.successOrFail, successOrFail) || + const DeepCollectionEquality() + .equals(other.successOrFail, successOrFail))); + } + + @override + int get hashCode => + runtimeType.hashCode ^ + const DeepCollectionEquality().hash(email) ^ + const DeepCollectionEquality().hash(password) ^ + const DeepCollectionEquality().hash(isSubmitting) ^ + const DeepCollectionEquality().hash(passwordError) ^ + const DeepCollectionEquality().hash(emailError) ^ + const DeepCollectionEquality().hash(successOrFail); + + @JsonKey(ignore: true) + @override + _$SignUpStateCopyWith<_SignUpState> get copyWith => + __$SignUpStateCopyWithImpl<_SignUpState>(this, _$identity); +} + +abstract class _SignUpState implements SignUpState { + const factory _SignUpState( + {String? email, + String? password, + required bool isSubmitting, + required Option passwordError, + required Option emailError, + required Option> successOrFail}) = + _$_SignUpState; + + @override + String? get email => throw _privateConstructorUsedError; + @override + String? get password => throw _privateConstructorUsedError; + @override + bool get isSubmitting => throw _privateConstructorUsedError; + @override + Option get passwordError => throw _privateConstructorUsedError; + @override + Option get emailError => throw _privateConstructorUsedError; + @override + Option> get successOrFail => + throw _privateConstructorUsedError; + @override + @JsonKey(ignore: true) + _$SignUpStateCopyWith<_SignUpState> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/app_flowy/lib/user/infrastructure/deps_resolver.dart b/app_flowy/lib/user/infrastructure/deps_resolver.dart index 3b7be71b37..e6aeadf02e 100644 --- a/app_flowy/lib/user/infrastructure/deps_resolver.dart +++ b/app_flowy/lib/user/infrastructure/deps_resolver.dart @@ -1,4 +1,5 @@ -import 'package:app_flowy/user/application/sign_in/sign_in_bloc.dart'; +import 'package:app_flowy/user/application/sign_in_bloc.dart'; +import 'package:app_flowy/user/application/sign_up_bloc.dart'; import 'package:app_flowy/user/domain/i_auth.dart'; import 'package:app_flowy/user/infrastructure/repos/auth_repo.dart'; import 'package:app_flowy/user/infrastructure/i_auth_impl.dart'; @@ -14,5 +15,6 @@ class UserDepsResolver { //Bloc getIt.registerFactory(() => SignInBloc(getIt())); + getIt.registerFactory(() => SignUpBloc(getIt())); } } diff --git a/app_flowy/lib/user/infrastructure/i_auth_impl.dart b/app_flowy/lib/user/infrastructure/i_auth_impl.dart index 5bdb8defcd..fe0d4b1533 100644 --- a/app_flowy/lib/user/infrastructure/i_auth_impl.dart +++ b/app_flowy/lib/user/infrastructure/i_auth_impl.dart @@ -1,3 +1,4 @@ +import 'package:app_flowy/user/presentation/sign_up/sign_up_screen.dart'; import 'package:app_flowy/workspace/infrastructure/repos/user_repo.dart'; import 'package:app_flowy/workspace/presentation/workspace/workspace_select_screen.dart'; import 'package:dartz/dartz.dart'; @@ -50,6 +51,10 @@ class AuthRouterImpl extends IAuthRouter { @override void showSignUpScreen(BuildContext context) { - // TODO: implement showSignUpScreen + Navigator.of(context).push( + PageRoutes.fade( + () => const SignUpScreen(), + ), + ); } } diff --git a/app_flowy/lib/user/presentation/sign_in/sign_in_screen.dart b/app_flowy/lib/user/presentation/sign_in_screen.dart similarity index 96% rename from app_flowy/lib/user/presentation/sign_in/sign_in_screen.dart rename to app_flowy/lib/user/presentation/sign_in_screen.dart index 1be8c59f21..490c5c1ff4 100644 --- a/app_flowy/lib/user/presentation/sign_in/sign_in_screen.dart +++ b/app_flowy/lib/user/presentation/sign_in_screen.dart @@ -1,5 +1,5 @@ import 'package:app_flowy/startup/startup.dart'; -import 'package:app_flowy/user/application/sign_in/sign_in_bloc.dart'; +import 'package:app_flowy/user/application/sign_in_bloc.dart'; import 'package:app_flowy/user/domain/i_auth.dart'; import 'package:app_flowy/user/presentation/sign_in/widgets/background.dart'; import 'package:flowy_infra/theme.dart'; @@ -63,9 +63,9 @@ class SignInForm extends StatelessWidget { Widget build(BuildContext context) { return Align( alignment: Alignment.center, - child: SignInFormContainer( + child: AuthFormContainer( children: [ - const SignInTitle( + const AuthFormTitle( title: 'Login to Appflowy', logoSize: Size(60, 60), ), @@ -128,7 +128,7 @@ class LoginButton extends StatelessWidget { final theme = context.watch(); return RoundedTextButton( title: 'Login', - height: 45, + height: 48, borderRadius: BorderRadius.circular(10), color: theme.main1, press: () { @@ -180,7 +180,7 @@ class PasswordTextField extends StatelessWidget { obscureText: true, obscureIcon: svgWidgetWithName("home/Hide.svg"), obscureHideIcon: svgWidgetWithName("home/Show.svg"), - hintText: 'password', + hintText: 'Password', normalBorderColor: theme.shader4, highlightBorderColor: theme.red, errorText: context @@ -210,7 +210,7 @@ class EmailTextField extends StatelessWidget { previous.emailError != current.emailError, builder: (context, state) { return RoundedInputField( - hintText: 'email', + hintText: 'Email', normalBorderColor: theme.shader4, highlightBorderColor: theme.red, errorText: context diff --git a/app_flowy/lib/user/presentation/sign_up/sign_up_screen.dart b/app_flowy/lib/user/presentation/sign_up/sign_up_screen.dart deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/app_flowy/lib/user/presentation/sign_up_screen.dart b/app_flowy/lib/user/presentation/sign_up_screen.dart new file mode 100644 index 0000000000..884133e495 --- /dev/null +++ b/app_flowy/lib/user/presentation/sign_up_screen.dart @@ -0,0 +1,202 @@ +import 'package:app_flowy/startup/startup.dart'; +import 'package:app_flowy/user/application/sign_up_bloc.dart'; +import 'package:app_flowy/user/presentation/widgets/background.dart'; +import 'package:flowy_infra/theme.dart'; +import 'package:flowy_infra_ui/widget/rounded_button.dart'; +import 'package:flowy_infra_ui/widget/rounded_input_field.dart'; +import 'package:flowy_infra_ui/widget/spacing.dart'; +import 'package:flowy_sdk/protobuf/flowy-user/errors.pb.dart'; +import 'package:flowy_sdk/protobuf/flowy-user/user_profile.pb.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:dartz/dartz.dart'; +import 'package:flowy_infra/image.dart'; + +class SignUpScreen extends StatelessWidget { + const SignUpScreen({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return BlocProvider( + create: (context) => getIt(), + child: BlocListener( + listener: (context, state) { + state.successOrFail.fold( + () => null, + (result) => _handleSuccessOrFail(result, context), + ); + }, + child: const Scaffold( + body: SignUpForm(), + ), + ), + ); + } + + void _handleSuccessOrFail( + Either result, BuildContext context) { + result.fold( + (user) => { + // router.showWorkspaceSelectScreen(context, user) + }, + (error) => _showErrorMessage(context, error.msg), + ); + } + + void _showErrorMessage(BuildContext context, String msg) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(msg), + ), + ); + } +} + +class SignUpForm extends StatelessWidget { + const SignUpForm({ + Key? key, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Align( + alignment: Alignment.center, + child: AuthFormContainer( + children: [ + const AuthFormTitle( + title: 'Sign Up to Appflowy', + logoSize: Size(60, 60), + ), + const VSpace(30), + const EmailTextField(), + const PasswordTextField(), + const PasswordTextField(hintText: "Repeate password"), + const VSpace(30), + const SignUpButton(), + const VSpace(10), + const SignUpPrompt(), + if (context.read().state.isSubmitting) ...[ + const SizedBox(height: 8), + const LinearProgressIndicator(value: null), + ] + ], + ), + ); + } +} + +class SignUpPrompt extends StatelessWidget { + const SignUpPrompt({ + Key? key, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + final theme = context.watch(); + return Row( + children: [ + Text("Already have an account", + style: TextStyle(color: theme.shader3, fontSize: 12)), + TextButton( + style: TextButton.styleFrom( + textStyle: const TextStyle(fontSize: 12), + ), + onPressed: () => Navigator.pop(context), + child: Text( + 'Sign In', + style: TextStyle(color: theme.main1), + ), + ), + ], + mainAxisAlignment: MainAxisAlignment.center, + ); + } +} + +class SignUpButton extends StatelessWidget { + const SignUpButton({ + Key? key, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + final theme = context.watch(); + return RoundedTextButton( + title: 'Get Started', + height: 48, + borderRadius: BorderRadius.circular(10), + color: theme.main1, + press: () { + context + .read() + .add(const SignUpEvent.signUpWithUserEmailAndPassword()); + }, + ); + } +} + +class PasswordTextField extends StatelessWidget { + final String hintText; + const PasswordTextField({ + Key? key, + this.hintText = "Password", + }) : super(key: key); + + @override + Widget build(BuildContext context) { + final theme = context.watch(); + return BlocBuilder( + buildWhen: (previous, current) => + previous.passwordError != current.passwordError, + builder: (context, state) { + return RoundedInputField( + obscureText: true, + obscureIcon: svgWidgetWithName("home/Hide.svg"), + obscureHideIcon: svgWidgetWithName("home/Show.svg"), + fontSize: 14, + fontWeight: FontWeight.w500, + hintText: hintText, + normalBorderColor: theme.shader4, + highlightBorderColor: theme.red, + errorText: context + .read() + .state + .passwordError + .fold(() => "", (error) => error), + onChanged: (value) => context + .read() + .add(SignUpEvent.passwordChanged(value)), + ); + }, + ); + } +} + +class EmailTextField extends StatelessWidget { + const EmailTextField({ + Key? key, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + final theme = context.watch(); + return BlocBuilder( + buildWhen: (previous, current) => + previous.emailError != current.emailError, + builder: (context, state) { + return RoundedInputField( + hintText: 'Email', + normalBorderColor: theme.shader4, + highlightBorderColor: theme.red, + errorText: context + .read() + .state + .emailError + .fold(() => "", (error) => error), + onChanged: (value) => + context.read().add(SignUpEvent.emailChanged(value)), + ); + }, + ); + } +} diff --git a/app_flowy/lib/user/presentation/sign_in/widgets/background.dart b/app_flowy/lib/user/presentation/widgets/background.dart similarity index 77% rename from app_flowy/lib/user/presentation/sign_in/widgets/background.dart rename to app_flowy/lib/user/presentation/widgets/background.dart index 492ec6b50b..76a8122029 100644 --- a/app_flowy/lib/user/presentation/sign_in/widgets/background.dart +++ b/app_flowy/lib/user/presentation/widgets/background.dart @@ -1,12 +1,14 @@ +import 'dart:math'; + import 'package:flowy_infra/image.dart'; import 'package:flowy_infra/theme.dart'; import 'package:flowy_infra_ui/widget/spacing.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; -class SignInFormContainer extends StatelessWidget { +class AuthFormContainer extends StatelessWidget { final List children; - const SignInFormContainer({ + const AuthFormContainer({ Key? key, required this.children, }) : super(key: key); @@ -15,7 +17,7 @@ class SignInFormContainer extends StatelessWidget { Widget build(BuildContext context) { final size = MediaQuery.of(context).size; return SizedBox( - width: size.width * 0.3, + width: min(size.width, 340), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: children, @@ -24,10 +26,10 @@ class SignInFormContainer extends StatelessWidget { } } -class SignInTitle extends StatelessWidget { +class AuthFormTitle extends StatelessWidget { final String title; final Size logoSize; - const SignInTitle({ + const AuthFormTitle({ Key? key, required this.title, required this.logoSize, @@ -40,7 +42,10 @@ class SignInTitle extends StatelessWidget { child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - svgWidgetWithName("small_logo.svg"), + SizedBox.fromSize( + size: const Size.square(40), + child: svgWidgetWithName("flowy_logo.svg"), + ), const VSpace(30), Text( title, diff --git a/app_flowy/packages/flowy_infra/lib/theme.dart b/app_flowy/packages/flowy_infra/lib/theme.dart index c431aee5c3..57f81cf180 100644 --- a/app_flowy/packages/flowy_infra/lib/theme.dart +++ b/app_flowy/packages/flowy_infra/lib/theme.dart @@ -12,19 +12,6 @@ class AppTheme { bool isDark; late Color surface; // - late Color accent1; - late Color accent1Dark; - late Color accent1Darker; - late Color accent2; - late Color accent3; - late Color grey; - late Color greyStrong; - late Color greyWeak; - late Color error; - late Color focus; - late Color txt; - late Color accentTxt; - late Color hover; late Color selector; late Color red; @@ -58,10 +45,7 @@ class AppTheme { late Color main2; /// Default constructor - AppTheme({this.isDark = true}) { - txt = isDark ? Colors.white : Colors.black; - accentTxt = isDark ? Colors.black : Colors.white; - } + AppTheme({this.isDark = true}); /// fromType factory constructor factory AppTheme.fromType(ThemeType t) { @@ -69,16 +53,6 @@ class AppTheme { case ThemeType.light: return AppTheme(isDark: false) ..surface = Colors.white - ..accent1 = const Color(0xff00a086) - ..accent1Dark = const Color(0xff00856f) - ..accent1Darker = const Color(0xff006b5a) - ..accent2 = const Color(0xfff09433) - ..accent3 = const Color(0xff5bc91a) - ..greyWeak = const Color(0xff909f9c) - ..grey = const Color(0xff515d5a) - ..greyStrong = const Color(0xff151918) - ..error = Colors.red.shade900 - ..focus = const Color(0xFFe0f8ff) ..hover = const Color(0xFFe0f8ff) // ..selector = const Color(0xfff2fcff) ..red = const Color(0xfffb006d) @@ -109,19 +83,7 @@ class AppTheme { case ThemeType.dark: return AppTheme(isDark: true) - ..bg1 = const Color(0xff121212) - ..bg2 = const Color(0xff2c2c2c) ..surface = const Color(0xff252525) - ..accent1 = const Color(0xff00a086) - ..accent1Dark = const Color(0xff00caa5) - ..accent1Darker = const Color(0xff00caa5) - ..accent2 = const Color(0xfff19e46) - ..accent3 = const Color(0xff5BC91A) - ..greyWeak = const Color(0xffa8b3b0) - ..grey = const Color(0xffced4d3) - ..greyStrong = const Color(0xffffffff) - ..error = const Color(0xffe55642) - ..focus = const Color(0xff0ee2b1) ..hover = const Color(0xFFe0f8ff) // ..selector = const Color(0xfff2fcff) ..red = const Color(0xfffb006d) @@ -153,8 +115,9 @@ class AppTheme { } ThemeData get themeData { - var t = ThemeData.from( + var t = ThemeData( textTheme: (isDark ? ThemeData.dark() : ThemeData.light()).textTheme, + textSelectionTheme: TextSelectionThemeData(cursorColor: main1), colorScheme: ColorScheme( brightness: isDark ? Brightness.dark : Brightness.light, primary: main1, @@ -164,15 +127,17 @@ class AppTheme { background: bg1, surface: surface, onBackground: bg1, - onSurface: txt, + onSurface: surface, onError: red, onPrimary: bg1, onSecondary: bg1, - error: error), + error: red), ); + return t.copyWith( materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, highlightColor: main1, + indicatorColor: main1, toggleableActiveColor: main1); } diff --git a/app_flowy/packages/flowy_infra_ui/lib/widget/rounded_input_field.dart b/app_flowy/packages/flowy_infra_ui/lib/widget/rounded_input_field.dart index a33d5bd902..974aa68ed1 100644 --- a/app_flowy/packages/flowy_infra_ui/lib/widget/rounded_input_field.dart +++ b/app_flowy/packages/flowy_infra_ui/lib/widget/rounded_input_field.dart @@ -1,7 +1,9 @@ +import 'package:flowy_infra/theme.dart'; import 'package:flowy_infra_ui/widget/rounded_button.dart'; import 'package:flowy_infra_ui/widget/text_field_container.dart'; import 'package:flutter/material.dart'; import 'package:flowy_infra/time/duration.dart'; +import 'package:provider/provider.dart'; // ignore: must_be_immutable class RoundedInputField extends StatefulWidget { @@ -10,11 +12,14 @@ class RoundedInputField extends StatefulWidget { final bool obscureText; final Widget? obscureIcon; final Widget? obscureHideIcon; + final FontWeight? fontWeight; + final double? fontSize; final Color normalBorderColor; final Color highlightBorderColor; final String errorText; final ValueChanged? onChanged; late bool enableObscure; + var _text = ""; RoundedInputField({ Key? key, @@ -26,6 +31,8 @@ class RoundedInputField extends StatefulWidget { this.onChanged, this.normalBorderColor = Colors.transparent, this.highlightBorderColor = Colors.transparent, + this.fontWeight = FontWeight.normal, + this.fontSize = 20, this.errorText = "", }) : super(key: key) { enableObscure = obscureText; @@ -38,6 +45,7 @@ class RoundedInputField extends StatefulWidget { class _RoundedInputFieldState extends State { @override Widget build(BuildContext context) { + final theme = context.watch(); final Icon? newIcon = widget.icon == null ? null : Icon( @@ -52,15 +60,23 @@ class _RoundedInputFieldState extends State { List children = [ TextFieldContainer( + height: 48, borderRadius: BorderRadius.circular(10), borderColor: borderColor, child: TextFormField( - onChanged: widget.onChanged, - cursorColor: const Color(0xFF6F35A5), + onChanged: (value) { + widget._text = value; + if (widget.onChanged != null) { + widget.onChanged!(value); + } + setState(() {}); + }, + cursorColor: theme.main1, obscureText: widget.enableObscure, decoration: InputDecoration( icon: newIcon, hintText: widget.hintText, + hintStyle: TextStyle(color: widget.normalBorderColor), border: InputBorder.none, suffixIcon: suffixIcon(), ), @@ -71,7 +87,10 @@ class _RoundedInputFieldState extends State { if (widget.errorText.isNotEmpty) { children.add(Text( widget.errorText, - style: TextStyle(color: widget.highlightBorderColor), + style: TextStyle( + color: widget.highlightBorderColor, + fontWeight: widget.fontWeight, + fontSize: widget.fontSize), )); } @@ -89,6 +108,10 @@ class _RoundedInputFieldState extends State { return null; } + if (widget._text.isEmpty) { + return SizedBox.fromSize(size: const Size.square(16)); + } + Widget? icon; if (widget.obscureText == true) { assert(widget.obscureIcon != null && widget.obscureHideIcon != null); @@ -104,7 +127,7 @@ class _RoundedInputFieldState extends State { } return RoundedImageButton( - size: 20, + size: 16, press: () { widget.enableObscure = !widget.enableObscure; setState(() {});