Skip to content

Commit

Permalink
[AN] feat: 사용자 프로필 페이지 에러 핸들링 (#447)
Browse files Browse the repository at this point in the history
  • Loading branch information
junjange authored Aug 17, 2024
1 parent efbae63 commit 80634f8
Show file tree
Hide file tree
Showing 15 changed files with 113 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -45,5 +45,17 @@ class MemberRepositoryImpl(
},
)

override suspend fun getMember(id: Long): Result<Member> = source.getMember(id = id).mapCatching { result -> result.toDomain() }
override suspend fun getMember(id: Long): DomainResult<Member, DataError.Network> =
source.getMember(id = id).fold(
onSuccess = { memberDto ->
DomainResult.Success(memberDto.toDomain())
},
onFailure = { e ->
if (e is ApiExceptionDto) {
DomainResult.Error(e.error.data.errorCode.toDomain())
} else {
DomainResult.Error(DataError.Network.NO_INTERNET)
}
},
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,17 @@ class PetRepositoryImpl(private val source: PetDataSource) : PetRepository {
},
)

override suspend fun getPets(id: Long): Result<List<Pet>> =
source.getPets(id = id).mapCatching { result -> result.map { petDto -> petDto.toDomain() } }
override suspend fun getPets(id: Long): DomainResult<List<Pet>, DataError.Network> =
source.getPets(id = id).fold(
onSuccess = { result ->
DomainResult.Success(result.map { petDto -> petDto.toDomain() })
},
onFailure = { e ->
if (e is ApiExceptionDto) {
DomainResult.Error(e.error.data.errorCode.toDomain())
} else {
DomainResult.Error(DataError.Network.NO_INTERNET)
}
},
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,5 @@ interface MemberRepository {

suspend fun getMemberMine(): DomainResult<Member, DataError.Network>

suspend fun getMember(id: Long): Result<Member>
suspend fun getMember(id: Long): DomainResult<Member, DataError.Network>
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,5 @@ interface PetRepository {
file: MultipartBody.Part?,
): DomainResult<Pet, DataError.Network>

suspend fun getPets(id: Long): Result<List<Pet>>
suspend fun getPets(id: Long): DomainResult<List<Pet>, DataError.Network>
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package com.happy.friendogly.domain.usecase

import com.happy.friendogly.domain.DomainResult
import com.happy.friendogly.domain.error.DataError
import com.happy.friendogly.domain.model.Member
import com.happy.friendogly.domain.repository.MemberRepository

class GetMemberUseCase(
private val repository: MemberRepository,
) {
suspend operator fun invoke(id: Long): Result<Member> = repository.getMember(id = id)
suspend operator fun invoke(id: Long): DomainResult<Member, DataError.Network> = repository.getMember(id = id)
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package com.happy.friendogly.domain.usecase

import com.happy.friendogly.domain.DomainResult
import com.happy.friendogly.domain.error.DataError
import com.happy.friendogly.domain.model.Pet
import com.happy.friendogly.domain.repository.PetRepository

class GetPetsUseCase(
private val repository: PetRepository,
) {
suspend operator fun invoke(id: Long): Result<List<Pet>> = repository.getPets(id = id)
suspend operator fun invoke(id: Long): DomainResult<List<Pet>, DataError.Network> = repository.getPets(id = id)
}
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,12 @@ class OtherProfileActivity :
}
}

viewModel.message.observeEvent(this) { message ->
when (message) {
is OtherProfileMessage.DefaultErrorMessage -> showToastMessage(getString(R.string.default_error_message))
}
}

viewModel.uiState.observe(this) { uiState ->
adapter.submitList(uiState.pets)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.happy.friendogly.presentation.ui.otherprofile

sealed interface OtherProfileMessage {
data object DefaultErrorMessage : OtherProfileMessage
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,10 @@ data class OtherProfileUiState(
val tag: String = "",
val profilePath: String? = null,
val pets: List<Pet> = emptyList(),
val otherProfileSkeleton: OtherProfileSkeleton = OtherProfileSkeleton(),
)

data class OtherProfileSkeleton(
val userProfile: Boolean = true,
val petProfile: Boolean = true,
)
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.createSavedStateHandle
import androidx.lifecycle.viewModelScope
import com.happy.friendogly.domain.fold
import com.happy.friendogly.domain.usecase.GetMemberUseCase
import com.happy.friendogly.domain.usecase.GetPetsUseCase
import com.happy.friendogly.presentation.base.BaseViewModel
Expand All @@ -14,7 +14,6 @@ import com.happy.friendogly.presentation.base.Event
import com.happy.friendogly.presentation.base.emit
import com.happy.friendogly.presentation.ui.petdetail.PetDetail
import com.happy.friendogly.presentation.ui.petdetail.PetsDetail
import kotlinx.coroutines.launch

class OtherProfileViewModel(
savedStateHandle: SavedStateHandle,
Expand All @@ -34,34 +33,49 @@ class OtherProfileViewModel(
MutableLiveData(null)
val navigateAction: LiveData<Event<OtherProfileNavigationAction>> get() = _navigateAction

private val _message: MutableLiveData<Event<OtherProfileMessage>> = MutableLiveData(null)
val message: LiveData<Event<OtherProfileMessage>> get() = _message

init {
fetchMember()
fetchPetMine()
}

private fun fetchMember() {
viewModelScope.launch {
getMemberUseCase(id = id).onSuccess { member ->
_uiState.value =
uiState.value?.copy(
nickname = member.name,
tag = member.tag,
profilePath = member.imageUrl,
)
}.onFailure {
// TODO 예외 처리
}
launch {
getMemberUseCase(id = id).fold(
onSuccess = { member ->
val state = uiState.value ?: return@launch
_uiState.value =
state.copy(
nickname = member.name,
tag = member.tag,
profilePath = member.imageUrl,
otherProfileSkeleton = state.otherProfileSkeleton.copy(userProfile = false),
)
},
onError = {
_message.emit(OtherProfileMessage.DefaultErrorMessage)
},
)
}
}

fun fetchPetMine() {
viewModelScope.launch {
getPetsUseCase(id = id).onSuccess { pets ->
_uiState.value =
uiState.value?.copy(pets = pets)
}.onFailure {
// TODO 예외 처리
}
launch {
getPetsUseCase(id = id).fold(
onSuccess = { pets ->
val state = uiState.value ?: return@launch
_uiState.value =
state.copy(
pets = pets,
otherProfileSkeleton = state.otherProfileSkeleton.copy(petProfile = false),
)
},
onError = {
_message.emit(OtherProfileMessage.DefaultErrorMessage)
},
)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ class PetDetailActivity : BaseActivity<ActivityPetDetailBinding>(R.layout.activi
private fun initObserve() {
viewModel.uiState.observe(this) { uiState ->
adapter.submitList(uiState.petsDetail.data)
binding.vpPetDetail.setCurrentItem(uiState.petsDetail.data.size, false)
binding.vpPetDetail.setCurrentItem(uiState.startPage, false)
}

viewModel.navigateAction.observeEvent(this) { action ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import kotlinx.serialization.Serializable

data class PetDetailUiState(
val petsDetail: PetsDetail = PetsDetail(emptyList()),
val startPage: Int = 0,
)

@Serializable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ class PetDetailViewModel(
requireNotNull(savedStateHandle.get<Int>(PetDetailActivity.PUT_EXTRA_CURRENT_PAGE))

val state = _uiState.value ?: return
_uiState.value = state.copy(petsDetail = petsDetail)
_currentPage.value = (MIDDLE_PAGE - (MIDDLE_PAGE) % petsDetail.data.size) + currentPage
_uiState.value = state.copy(petsDetail = petsDetail, startPage = currentPage)
_currentPage.value = currentPage
}

fun updateCurrentPage(page: Int) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ import androidx.recyclerview.widget.RecyclerView
import com.happy.friendogly.databinding.ItemPetDetailBinding
import com.happy.friendogly.presentation.ui.petdetail.PetDetail

class PetDetailAdapter : ListAdapter<PetDetail, PetDetailAdapter.ViewHolder>(PetDetailItemDiffCallback) {
class PetDetailAdapter :
ListAdapter<PetDetail, PetDetailAdapter.ViewHolder>(PetDetailItemDiffCallback) {
init {
setHasStableIds(true)
}
Expand All @@ -22,13 +23,11 @@ class PetDetailAdapter : ListAdapter<PetDetail, PetDetailAdapter.ViewHolder>(Pet
return ViewHolder(binding)
}

override fun getItemCount(): Int = Int.MAX_VALUE

override fun onBindViewHolder(
holder: ViewHolder,
position: Int,
) {
holder.bind(getItem(position % currentList.size))
holder.bind(getItem(position))
}

class ViewHolder(private val binding: ItemPetDetailBinding) :
Expand Down
21 changes: 21 additions & 0 deletions android/app/src/main/res/layout/activity_other_profile.xml
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,15 @@

</androidx.constraintlayout.widget.ConstraintLayout>

<include
layout="@layout/include_skeleton_user_profile"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="@{vm.uiState.otherProfileSkeleton.userProfile == true ? View.VISIBLE : View.GONE}"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:visibility="gone" />


<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/constraintLayout_pet_profile"
Expand All @@ -147,6 +156,7 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />


<androidx.viewpager2.widget.ViewPager2
android:id="@+id/vp_pet_profile"
android:layout_width="0dp"
Expand Down Expand Up @@ -200,6 +210,17 @@
app:layout_constraintStart_toStartOf="@+id/iv_empty_pet"
app:layout_constraintTop_toBottomOf="@+id/iv_empty_pet" />

<include
layout="@layout/include_skeleton_pet_profile"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:visibility="@{vm.uiState.otherProfileSkeleton.petProfile == true ? View.VISIBLE : View.GONE}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tv_pet_profile_title"
tools:visibility="gone" />


</androidx.constraintlayout.widget.ConstraintLayout>

Expand Down

0 comments on commit 80634f8

Please sign in to comment.