diff --git a/app/src/main/java/com/isolaatti/profile/data/remote/ProfileApi.kt b/app/src/main/java/com/isolaatti/profile/data/remote/ProfileApi.kt index e6f4c7a..224c892 100644 --- a/app/src/main/java/com/isolaatti/profile/data/remote/ProfileApi.kt +++ b/app/src/main/java/com/isolaatti/profile/data/remote/ProfileApi.kt @@ -1,6 +1,7 @@ package com.isolaatti.profile.data.remote import retrofit2.Call +import retrofit2.http.Body import retrofit2.http.GET import retrofit2.http.POST import retrofit2.http.Path @@ -14,4 +15,7 @@ interface ProfileApi { @POST("EditProfile/SetProfilePhoto") fun setProfileImage(@Query("imageId") imageId: String): Call + @POST("EditProfile/UpdateProfile") + fun updateProfile(@Body updateProfileDto: UpdateProfileDto): Call + } \ No newline at end of file diff --git a/app/src/main/java/com/isolaatti/profile/data/remote/UpdateProfileDto.kt b/app/src/main/java/com/isolaatti/profile/data/remote/UpdateProfileDto.kt new file mode 100644 index 0000000..2b96483 --- /dev/null +++ b/app/src/main/java/com/isolaatti/profile/data/remote/UpdateProfileDto.kt @@ -0,0 +1,6 @@ +package com.isolaatti.profile.data.remote + +data class UpdateProfileDto( + val newDescription: String, + val newUsername: String +) \ No newline at end of file diff --git a/app/src/main/java/com/isolaatti/profile/data/repository/ProfileRepositoryImpl.kt b/app/src/main/java/com/isolaatti/profile/data/repository/ProfileRepositoryImpl.kt index 65ee69e..f589671 100644 --- a/app/src/main/java/com/isolaatti/profile/data/repository/ProfileRepositoryImpl.kt +++ b/app/src/main/java/com/isolaatti/profile/data/repository/ProfileRepositoryImpl.kt @@ -3,6 +3,7 @@ package com.isolaatti.profile.data.repository import android.util.Log import com.isolaatti.images.common.domain.entity.Image import com.isolaatti.profile.data.remote.ProfileApi +import com.isolaatti.profile.data.remote.UpdateProfileDto import com.isolaatti.profile.domain.ProfileRepository import com.isolaatti.profile.domain.entity.UserProfile import com.isolaatti.utils.Resource @@ -45,4 +46,19 @@ class ProfileRepositoryImpl @Inject constructor(private val profileApi: ProfileA emit(Resource.Error(Resource.Error.ErrorType.NetworkError)) } } + + override fun updateProfile(newDisplayName: String, newDescription: String): Flow> = flow { + try { + val result = profileApi.updateProfile(UpdateProfileDto(newDescription, newDisplayName)).awaitResponse() + + if(result.isSuccessful) { + emit(Resource.Success(true)) + } else { + emit(Resource.Error(Resource.Error.mapErrorCode(result.code()))) + } + } catch (e: Exception) { + Log.e("ProfileRepositoryImpl", e.message.toString()) + emit(Resource.Error(Resource.Error.ErrorType.NetworkError)) + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/isolaatti/profile/domain/ProfileRepository.kt b/app/src/main/java/com/isolaatti/profile/domain/ProfileRepository.kt index ef09dd8..8fc9d0e 100644 --- a/app/src/main/java/com/isolaatti/profile/domain/ProfileRepository.kt +++ b/app/src/main/java/com/isolaatti/profile/domain/ProfileRepository.kt @@ -1,6 +1,7 @@ package com.isolaatti.profile.domain import com.isolaatti.images.common.domain.entity.Image +import com.isolaatti.profile.data.remote.UpdateProfileDto import com.isolaatti.profile.domain.entity.UserProfile import com.isolaatti.utils.Resource import kotlinx.coroutines.flow.Flow @@ -9,4 +10,6 @@ interface ProfileRepository { fun getProfile(userId: Int): Flow> fun setProfileImage(image: Image): Flow> + + fun updateProfile(newDisplayName: String, newDescription: String): Flow> } \ No newline at end of file diff --git a/app/src/main/java/com/isolaatti/profile/domain/entity/UserProfile.kt b/app/src/main/java/com/isolaatti/profile/domain/entity/UserProfile.kt index f6ecc52..bd3026a 100644 --- a/app/src/main/java/com/isolaatti/profile/domain/entity/UserProfile.kt +++ b/app/src/main/java/com/isolaatti/profile/domain/entity/UserProfile.kt @@ -8,7 +8,7 @@ import java.io.Serializable data class UserProfile( override val userId: Int, - val name: String, + var name: String, val email: String?, val numberOfFollowers: Int, val numberOfFollowing: Int, @@ -18,7 +18,7 @@ data class UserProfile( var followingThisUser: Boolean, val thisUserIsFollowingMe: Boolean, val profileImageId: String?, - val descriptionText: String?, + var descriptionText: String?, val descriptionAudioId: String?, val descriptionAudio: Audio? ) : Ownable, Serializable { diff --git a/app/src/main/java/com/isolaatti/profile/presentation/EditProfileContract.kt b/app/src/main/java/com/isolaatti/profile/presentation/EditProfileContract.kt index 268c03e..5f08cea 100644 --- a/app/src/main/java/com/isolaatti/profile/presentation/EditProfileContract.kt +++ b/app/src/main/java/com/isolaatti/profile/presentation/EditProfileContract.kt @@ -8,9 +8,11 @@ import androidx.activity.result.contract.ActivityResultContract import com.isolaatti.profile.domain.entity.UserProfile import com.isolaatti.profile.ui.EditProfileActivity -class EditProfileContract : ActivityResultContract() { - override fun createIntent(context: Context, input: Void?): Intent { - return Intent(context, EditProfileActivity::class.java) +class EditProfileContract : ActivityResultContract() { + override fun createIntent(context: Context, input: UserProfile): Intent { + return Intent(context, EditProfileActivity::class.java).apply { + putExtra(EditProfileActivity.EXTRA_IN_USER_PROFILE, input) + } } override fun parseResult(resultCode: Int, intent: Intent?): UserProfile? { diff --git a/app/src/main/java/com/isolaatti/profile/presentation/EditProfileViewModel.kt b/app/src/main/java/com/isolaatti/profile/presentation/EditProfileViewModel.kt index 75320ae..04437d9 100644 --- a/app/src/main/java/com/isolaatti/profile/presentation/EditProfileViewModel.kt +++ b/app/src/main/java/com/isolaatti/profile/presentation/EditProfileViewModel.kt @@ -1,9 +1,49 @@ package com.isolaatti.profile.presentation +import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.isolaatti.profile.domain.ProfileRepository +import com.isolaatti.utils.Resource import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.launch import javax.inject.Inject @HiltViewModel -class EditProfileViewModel @Inject constructor(): ViewModel() { +class EditProfileViewModel @Inject constructor(private val profileRepository: ProfileRepository): ViewModel() { + + var displayName = "" + set(value) { + field = value + validate() + } + var description = "" + set(value) { + field = value + validate() + } + + val isValid: MutableLiveData = MutableLiveData() + private fun validate() { + isValid.value = displayName.length in 1..20 && description.length <= 300 + } + + val updateResult: MutableLiveData> = MutableLiveData() + + fun updateProfile() { + if(displayName.isEmpty() || description.isEmpty()) { + return + } + + viewModelScope.launch { + profileRepository.updateProfile(displayName, description).onEach { + updateResult.postValue(it) + }.flowOn(Dispatchers.IO).launchIn(this) + } + } + } \ No newline at end of file diff --git a/app/src/main/java/com/isolaatti/profile/presentation/ProfileViewModel.kt b/app/src/main/java/com/isolaatti/profile/presentation/ProfileViewModel.kt index b054fa7..1fcedbd 100644 --- a/app/src/main/java/com/isolaatti/profile/presentation/ProfileViewModel.kt +++ b/app/src/main/java/com/isolaatti/profile/presentation/ProfileViewModel.kt @@ -139,4 +139,8 @@ class ProfileViewModel @Inject constructor( }.flowOn(Dispatchers.IO).launchIn(this) } } + + fun setProfile(profile: UserProfile) { + _profile.value = profile + } } \ No newline at end of file diff --git a/app/src/main/java/com/isolaatti/profile/ui/EditProfileActivity.kt b/app/src/main/java/com/isolaatti/profile/ui/EditProfileActivity.kt index 072b2c5..d986333 100644 --- a/app/src/main/java/com/isolaatti/profile/ui/EditProfileActivity.kt +++ b/app/src/main/java/com/isolaatti/profile/ui/EditProfileActivity.kt @@ -1,23 +1,93 @@ package com.isolaatti.profile.ui +import android.content.Intent +import android.os.Build import android.os.Bundle +import androidx.activity.viewModels +import androidx.core.widget.doOnTextChanged import com.isolaatti.common.IsolaattiBaseActivity import com.isolaatti.databinding.ActivityEditProfileBinding +import com.isolaatti.profile.domain.entity.UserProfile +import com.isolaatti.profile.presentation.EditProfileViewModel +import com.isolaatti.utils.Resource import dagger.hilt.android.AndroidEntryPoint @AndroidEntryPoint class EditProfileActivity : IsolaattiBaseActivity() { private lateinit var binding: ActivityEditProfileBinding + private val viewModel: EditProfileViewModel by viewModels() + private var inputProfile: UserProfile? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityEditProfileBinding.inflate(layoutInflater) setContentView(binding.root) + + setupListeners() + setupObservers() + + inputProfile = if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + intent.extras?.getSerializable(EXTRA_IN_USER_PROFILE, UserProfile::class.java) + } else { + intent.extras?.getSerializable(EXTRA_IN_USER_PROFILE) as UserProfile + } + + if(inputProfile != null) { + binding.displayName.editText?.setText(inputProfile!!.name) + binding.description.editText?.setText(inputProfile!!.descriptionText) + } + + } + + private fun setupListeners() { + binding.toolbar.setNavigationOnClickListener { + finish() + } + + binding.displayName.editText?.doOnTextChanged { text, _, _, _ -> + viewModel.displayName = text.toString() + } + + binding.description.editText?.doOnTextChanged { text, _, _, _ -> + viewModel.description = text.toString() + } + + binding.acceptButton.setOnClickListener { + viewModel.updateProfile() + } + } + + private fun setupObservers() { + viewModel.isValid.observe(this) { + binding.acceptButton.isEnabled = it + } + + viewModel.updateResult.observe(this) { + when(it) { + is Resource.Error -> {} + is Resource.Loading -> {} + is Resource.Success -> { + inputProfile?.apply { + name = viewModel.displayName + descriptionText = viewModel.description + } + if(inputProfile != null) { + val resultIntent = Intent().apply { + putExtra(EXTRA_OUT_USER_PROFILE, inputProfile) + } + setResult(RESULT_OK, resultIntent) + } + + finish() + } + } + } } companion object { - const val EXTRA_OUT_USER_PROFILE = "user_profile" + const val EXTRA_OUT_USER_PROFILE = "out_user_profile" + const val EXTRA_IN_USER_PROFILE = "in_user_profile" } } \ No newline at end of file diff --git a/app/src/main/java/com/isolaatti/profile/ui/ProfileMainFragment.kt b/app/src/main/java/com/isolaatti/profile/ui/ProfileMainFragment.kt index 58c4fd4..0c7c1c3 100644 --- a/app/src/main/java/com/isolaatti/profile/ui/ProfileMainFragment.kt +++ b/app/src/main/java/com/isolaatti/profile/ui/ProfileMainFragment.kt @@ -99,8 +99,10 @@ class ProfileMainFragment : Fragment() { } } - private val editProfile = registerForActivityResult(EditProfileContract()) { - + private val editProfile = registerForActivityResult(EditProfileContract()) { updatedProfile -> + if(updatedProfile != null) { + viewModel.setProfile(updatedProfile) + } } private val audioPlayerConnectorListener = object: AudioPlayerConnector.Listener { @@ -278,7 +280,8 @@ class ProfileMainFragment : Fragment() { viewBinding.topAppBar.setOnMenuItemClickListener { when(it.itemId) { R.id.edit_profile -> { - editProfile.launch(null) + viewModel.profile.value?.let { profile -> editProfile.launch(profile) } + true } R.id.user_link_menu_item -> { diff --git a/app/src/main/res/layout/activity_edit_profile.xml b/app/src/main/res/layout/activity_edit_profile.xml index 7fd71ed..4a4cf1c 100644 --- a/app/src/main/res/layout/activity_edit_profile.xml +++ b/app/src/main/res/layout/activity_edit_profile.xml @@ -1,6 +1,68 @@ - + android:layout_height="match_parent" + xmlns:app="http://schemas.android.com/apk/res-auto"> - \ No newline at end of file + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 6aeeb73..55ec458 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -136,4 +136,5 @@ Create a new discussion Edit profile Remove image? This will not delete your image, but only unset it as profile image + Description \ No newline at end of file