diff --git a/lib/application/user/profile/profile_bloc.dart b/lib/application/user/profile/profile_bloc.dart index 99784a81..fe7ce549 100644 --- a/lib/application/user/profile/profile_bloc.dart +++ b/lib/application/user/profile/profile_bloc.dart @@ -25,24 +25,45 @@ class ProfileBloc extends Bloc { await userOrFailure.fold( (failure) => const ProfileState( userProfile: null, - isEditing: false, + isPicEditing: false, + isBioEditing: false, ), (userProfile) => state.copyWith( userProfile: userProfile, - isEditing: false, + isPicEditing: false, + isBioEditing: false, ), ), ); }); - on((event, emit) { - emit(state.copyWith(isEditing: true)); + on((event, emit) { + emit(state.copyWith(isBioEditing: true)); }); - on((event, emit) async { + on((event, emit) { + emit(state.copyWith(isPicEditing: true)); + }); + + on((event, emit) async { if (event.bio != null) { await _profileRepository.saveProfile(bio: event.bio); } + + final userOrFailure = await _profileRepository.getUserProfile(); + + emit( + userOrFailure.fold( + (failure) => state.copyWith(isBioEditing: false), + (userProfile) => state.copyWith( + userProfile: userProfile, + isBioEditing: false, + ), + ), + ); + }); + + on((event, emit) async { final wasImageUpdated = event.image != null; if (wasImageUpdated) { await _avatarRepository.uploadAvatar(event.image!); @@ -53,18 +74,18 @@ class ProfileBloc extends Bloc { emit( userOrFailure.fold( - (failure) => state.copyWith(isEditing: false), + (failure) => state.copyWith(isPicEditing: false), (userProfile) => state.copyWith( userProfile: userProfile, - isEditing: false, + isPicEditing: false, wasProfilePictureUpdated: wasImageUpdated, ), ), ); }); - on((event, emit) { - emit(state.copyWith(isEditing: false)); + on((event, emit) { + emit(state.copyWith(isPicEditing: false)); }); } } diff --git a/lib/application/user/profile/profile_event.dart b/lib/application/user/profile/profile_event.dart index 4780c65e..a7f0dac6 100644 --- a/lib/application/user/profile/profile_event.dart +++ b/lib/application/user/profile/profile_event.dart @@ -4,16 +4,25 @@ abstract class ProfileEvent {} class GetUserProfile extends ProfileEvent {} -class EditProfile extends ProfileEvent {} +class EditBio extends ProfileEvent {} -class SaveProfile extends ProfileEvent { +class SaveBio extends ProfileEvent { final String? bio; + SaveBio({ + this.bio, + }); +} + +class CancelBio extends ProfileEvent {} + +class EditProfilePic extends ProfileEvent {} + +class SaveProfilePic extends ProfileEvent { final File? image; - SaveProfile({ - this.bio, + SaveProfilePic({ this.image, }); } -class CancelEditProfile extends ProfileEvent {} +class CancelEditProfilePic extends ProfileEvent {} diff --git a/lib/application/user/profile/profile_state.dart b/lib/application/user/profile/profile_state.dart index 57646fcf..da4fbbd1 100644 --- a/lib/application/user/profile/profile_state.dart +++ b/lib/application/user/profile/profile_state.dart @@ -3,33 +3,39 @@ part of 'profile_bloc.dart'; class ProfileState extends Equatable { const ProfileState({ required this.userProfile, - this.isEditing, + this.isPicEditing, + this.isBioEditing, this.wasProfilePictureUpdated, }); final UserProfile? userProfile; - final bool? isEditing; + final bool? isPicEditing; + final bool? isBioEditing; final bool? wasProfilePictureUpdated; factory ProfileState.initial() => const ProfileState( userProfile: null, - isEditing: false, + isPicEditing: false, + isBioEditing: false, wasProfilePictureUpdated: false, ); ProfileState copyWith({ UserProfile? userProfile, - bool? isEditing, + bool? isPicEditing, + bool? isBioEditing, bool? wasProfilePictureUpdated, }) { return ProfileState( userProfile: userProfile ?? this.userProfile, - isEditing: isEditing ?? this.isEditing, + isPicEditing: isPicEditing ?? this.isPicEditing, + isBioEditing: isBioEditing ?? this.isBioEditing, wasProfilePictureUpdated: wasProfilePictureUpdated ?? false, // Reset state implicitly ); } @override - List get props => [userProfile, isEditing, wasProfilePictureUpdated]; + List get props => + [userProfile, isPicEditing, isBioEditing, wasProfilePictureUpdated]; } diff --git a/lib/presentation/profile/profile_screen.dart b/lib/presentation/profile/profile_screen.dart index 52fd7b0c..bbdfd425 100644 --- a/lib/presentation/profile/profile_screen.dart +++ b/lib/presentation/profile/profile_screen.dart @@ -129,21 +129,59 @@ class _UserProfilePageState extends State { maxRadius: 50, ), ), - if (state.isEditing == true) ...[ + if (state.userProfile != null) ...[ Positioned( bottom: 0, right: 0, child: FloatingActionButton( - onPressed: () => - PhotoSelector.showPhotoSelector( - context, - onSelected: (image) { - setState(() => _image = image); - }, - ), + onPressed: () { + PhotoSelector.showPhotoSelector( + context, + onSelected: (image) { + setState(() => _image = image); + }, + ); + context + .read() + .add(EditProfilePic()); + }, + backgroundColor: kAccentColor, + mini: true, + child: const Icon(Icons.edit), + ), + ), + ], + if (state.isPicEditing == true) ...[ + Positioned( + bottom: 0, + right: 0, + child: FloatingActionButton( + onPressed: () { + BlocProvider.of(context) + .add( + SaveProfilePic( + image: _image, + ), + ); + }, backgroundColor: kAccentColor, mini: true, - child: const Icon(Icons.add), + child: const Icon(Icons.check), + ), + ), + Positioned( + bottom: 0, + left: 0, + child: FloatingActionButton( + onPressed: () { + BlocProvider.of(context) + .add(CancelEditProfilePic()); + + _image = null; + }, + backgroundColor: kErrorColor, + mini: true, + child: const Icon(Icons.close), ), ), ] @@ -162,17 +200,59 @@ class _UserProfilePageState extends State { ), if (state.userProfile != null) ...[ const SizedBox(height: 40), - const Text( - 'About me', - style: TextStyle( - fontWeight: FontWeight.w700, - fontSize: 11, - color: Color(0xFF666666), - ), - textAlign: TextAlign.left, + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text( + 'About me', + style: TextStyle( + fontWeight: FontWeight.w700, + fontSize: 11, + color: Color(0xFF666666), + ), + textAlign: TextAlign.left, + ), + TextButton( + key: const Key('save_edit_bio_button'), + style: ButtonStyle( + shape: MaterialStateProperty.all( + RoundedRectangleBorder( + borderRadius: + BorderRadius.circular(200), + ), + ), + ), + onPressed: () { + if (state.isBioEditing == true) { + /// TODO: Implement save profile image + + BlocProvider.of(context).add( + SaveBio( + bio: bioController.text, + ), + ); + } else { + context + .read() + .add(EditBio()); + } + }, + child: Text( + state.isBioEditing == true + ? 'Save changes' + : 'Edit Bio', + style: const TextStyle( + fontSize: 11, + color: kAccentColor, + fontWeight: FontWeight.w700, + ), + ), + ), + ], ), const SizedBox(height: 10), - if (state.isEditing == true) ...[ + if (state.isBioEditing == true) ...[ + //idhr bio editing start Row( children: [ Expanded( @@ -215,8 +295,9 @@ class _UserProfilePageState extends State { ), const SizedBox(height: 4), Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, children: [ - const SizedBox(width: 16), Text( 'Maximum 150 characters', style: Theme.of(context) @@ -265,90 +346,6 @@ class _UserProfilePageState extends State { ], ), ), - const SizedBox(height: 40), - TextButton( - key: const Key('save_edit_button'), - style: ButtonStyle( - overlayColor: MaterialStateColor.resolveWith( - (states) => state.isEditing == true - ? Colors.white.withOpacity(0.1) - : kAccentColor.withOpacity(0.1), - ), - backgroundColor: state.isEditing == true - ? MaterialStateProperty.all( - kAccentColor, - ) - : null, - minimumSize: MaterialStateProperty.all( - const Size(double.infinity * 0.75, 52), - ), - shape: MaterialStateProperty.all( - RoundedRectangleBorder( - borderRadius: BorderRadius.circular(200), - side: const BorderSide( - color: kAccentColor, - ), - ), - ), - ), - onPressed: () { - if (state.isEditing == true) { - BlocProvider.of(context).add( - SaveProfile( - bio: bioController.text, - image: _image, - ), - ); - } else { - context - .read() - .add(EditProfile()); - } - }, - child: Text( - state.isEditing == true - ? 'Save changes' - : 'Edit profile', - style: TextStyle( - color: state.isEditing == true - ? Colors.white - : kAccentColor, - fontWeight: FontWeight.w700, - ), - ), - ), - if (state.isEditing == true) ...[ - const SizedBox(height: 10), - TextButton( - key: const Key('cancel_edit_button'), - style: ButtonStyle( - minimumSize: MaterialStateProperty.all( - const Size(double.infinity * 0.75, 52), - ), - shape: MaterialStateProperty.all( - RoundedRectangleBorder( - borderRadius: BorderRadius.circular(200), - ), - ), - ), - onPressed: () { - BlocProvider.of(context) - .add(CancelEditProfile()); - - _image = null; - bioController.value = TextEditingValue( - text: state.userProfile?.profile.bio ?? '', - ); - }, - child: const Text( - 'Cancel', - style: TextStyle( - color: kAccentColor, - fontWeight: FontWeight.w700, - ), - ), - ), - ], ] else ...[ // TODO only for MVP (remove later) const SizedBox(height: 40), diff --git a/test/application/user/profile/profile_bloc_test.dart b/test/application/user/profile/profile_bloc_test.dart index 5183cb8d..350be4e5 100644 --- a/test/application/user/profile/profile_bloc_test.dart +++ b/test/application/user/profile/profile_bloc_test.dart @@ -40,8 +40,9 @@ void main() { expect: () => const [ ProfileState( userProfile: userProfile, - isEditing: false, + isPicEditing: false, wasProfilePictureUpdated: false, + isBioEditing: false, ) ], verify: (_) => verify(() => profileRepository.getUserProfile()).called(1), @@ -59,7 +60,8 @@ void main() { expect: () => const [ ProfileState( userProfile: null, - isEditing: false, + isPicEditing: false, + isBioEditing: false, ) ], verify: (_) => verify(() => profileRepository.getUserProfile()).called(1),