feature: editar perfil
This commit is contained in:
parent
458de335d4
commit
6b3a3b904c
@ -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<Void>
|
||||
|
||||
@POST("EditProfile/UpdateProfile")
|
||||
fun updateProfile(@Body updateProfileDto: UpdateProfileDto): Call<UserProfileDto>
|
||||
|
||||
}
|
||||
@ -0,0 +1,6 @@
|
||||
package com.isolaatti.profile.data.remote
|
||||
|
||||
data class UpdateProfileDto(
|
||||
val newDescription: String,
|
||||
val newUsername: String
|
||||
)
|
||||
@ -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<Resource<Boolean>> = 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))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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<Resource<UserProfile>>
|
||||
|
||||
fun setProfileImage(image: Image): Flow<Resource<Boolean>>
|
||||
|
||||
fun updateProfile(newDisplayName: String, newDescription: String): Flow<Resource<Boolean>>
|
||||
}
|
||||
@ -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 {
|
||||
|
||||
@ -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<Void?, UserProfile?>() {
|
||||
override fun createIntent(context: Context, input: Void?): Intent {
|
||||
return Intent(context, EditProfileActivity::class.java)
|
||||
class EditProfileContract : ActivityResultContract<UserProfile, UserProfile?>() {
|
||||
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? {
|
||||
|
||||
@ -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<Boolean> = MutableLiveData()
|
||||
private fun validate() {
|
||||
isValid.value = displayName.length in 1..20 && description.length <= 300
|
||||
}
|
||||
|
||||
val updateResult: MutableLiveData<Resource<Boolean>> = MutableLiveData()
|
||||
|
||||
fun updateProfile() {
|
||||
if(displayName.isEmpty() || description.isEmpty()) {
|
||||
return
|
||||
}
|
||||
|
||||
viewModelScope.launch {
|
||||
profileRepository.updateProfile(displayName, description).onEach {
|
||||
updateResult.postValue(it)
|
||||
}.flowOn(Dispatchers.IO).launchIn(this)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -139,4 +139,8 @@ class ProfileViewModel @Inject constructor(
|
||||
}.flowOn(Dispatchers.IO).launchIn(this)
|
||||
}
|
||||
}
|
||||
|
||||
fun setProfile(profile: UserProfile) {
|
||||
_profile.value = profile
|
||||
}
|
||||
}
|
||||
@ -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"
|
||||
}
|
||||
}
|
||||
@ -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 -> {
|
||||
|
||||
@ -1,6 +1,68 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
android:layout_height="match_parent"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:id="@+id/appBarLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:title="@string/edit_profile"
|
||||
app:navigationIcon="@drawable/baseline_close_24"/>
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<ScrollView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintTop_toBottomOf="@id/appBarLayout"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent">
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/displayName"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintTop_toBottomOf="@id/appBarLayout"
|
||||
android:layout_marginTop="32dp"
|
||||
android:layout_marginHorizontal="32dp"
|
||||
android:hint="@string/display_name">
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/description"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintTop_toBottomOf="@id/displayName"
|
||||
android:layout_marginHorizontal="32dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:hint="@string/description">
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:lines="6"/>
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/acceptButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="32dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="@string/accept"/>
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@ -136,4 +136,5 @@
|
||||
<string name="create_a_new_discussion">Create a new discussion</string>
|
||||
<string name="edit_profile">Edit profile</string>
|
||||
<string name="remove_image_message_confirmation">Remove image? This will not delete your image, but only unset it as profile image</string>
|
||||
<string name="description">Description</string>
|
||||
</resources>
|
||||
Loading…
x
Reference in New Issue
Block a user