Skip to content

Commit

Permalink
Merge pull request #303 from team-peekabook/feature/#299-delete-recom…
Browse files Browse the repository at this point in the history
…mend

#299 [feat] 추천한/추천받은 책 삭제
  • Loading branch information
2zerozu authored Feb 25, 2024
2 parents 15fafe7 + a541b72 commit 0502928
Show file tree
Hide file tree
Showing 42 changed files with 438 additions and 44 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,9 @@ class RecommendRepositoryImpl @Inject constructor(
friendId
)
}.map { response -> response.success }

override suspend fun deleteRecommend(recommendId: Int): Result<Unit> =
kotlin.runCatching {
recommendDataSource.deleteRecommend(recommendId)
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package com.sopt.peekabookaos.data.service

import com.sopt.peekabookaos.data.entity.BaseResponse
import com.sopt.peekabookaos.data.entity.NoResponse
import com.sopt.peekabookaos.data.entity.request.RecommendationRequest
import com.sopt.peekabookaos.data.entity.response.RecommendResponse
import com.sopt.peekabookaos.data.entity.response.RecommendationResponse
import retrofit2.http.Body
import retrofit2.http.DELETE
import retrofit2.http.GET
import retrofit2.http.POST
import retrofit2.http.Path
Expand All @@ -18,4 +20,9 @@ interface RecommendService {
@Body body: RecommendationRequest,
@Path("friendId") friendId: Int
): BaseResponse<RecommendationResponse>

@DELETE("recommend/{recommendId}")
suspend fun deleteRecommend(
@Path("recommendId") recommendId: Int
): BaseResponse<NoResponse>
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.sopt.peekabookaos.data.source.remote

import com.sopt.peekabookaos.data.entity.BaseResponse
import com.sopt.peekabookaos.data.entity.NoResponse
import com.sopt.peekabookaos.data.entity.request.RecommendationRequest
import com.sopt.peekabookaos.data.entity.response.RecommendResponse
import com.sopt.peekabookaos.data.entity.response.RecommendationResponse
Expand Down Expand Up @@ -31,4 +32,7 @@ data class RecommendDataSource @Inject constructor(
),
friendId
)

suspend fun deleteRecommend(recommendId: Int): BaseResponse<NoResponse> =
recommendService.deleteRecommend(recommendId)
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@ data class Recommend(
val bookId: Int = -1,
val bookTitle: String = "",
val author: String = "",
val bookImage: String = ""
val bookImage: String = "",
val isEditMode: Boolean = false
)
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,6 @@ interface RecommendRepository {
publisher: String,
friendId: Int
): Result<Boolean>

suspend fun deleteRecommend(recommendId: Int): Result<Unit>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.sopt.peekabookaos.domain.usecase

import com.sopt.peekabookaos.domain.repository.RecommendRepository
import javax.inject.Inject

class DeleteRecommendUseCase @Inject constructor(
private val recommendRepository: RecommendRepository
) {
suspend operator fun invoke(recommendId: Int) = recommendRepository.deleteRecommend(recommendId)
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ import com.sopt.peekabookaos.databinding.ItemRecommendBinding
import com.sopt.peekabookaos.domain.entity.Recommend
import com.sopt.peekabookaos.util.ItemDiffCallback

class BookRecommendAdapter :
ListAdapter<Recommend, BookRecommendAdapter.BookRecommendationViewHolder>(recommendationDiffUtil) {
class BookRecommendAdapter(
private val onClickDelete: (Int) -> Unit
) : ListAdapter<Recommend, BookRecommendAdapter.BookRecommendationViewHolder>(recommendationDiffUtil) {

override fun onCreateViewHolder(
parent: ViewGroup,
Expand All @@ -22,18 +23,25 @@ class BookRecommendAdapter :
false
)
return BookRecommendationViewHolder(
itemRecommendRecommendedBinding
itemRecommendRecommendedBinding,
onClickDelete
)
}

override fun onBindViewHolder(holder: BookRecommendationViewHolder, position: Int) {
holder.onBind(getItem(position))
}

class BookRecommendationViewHolder(private val binding: ItemRecommendBinding) :
class BookRecommendationViewHolder(
private val binding: ItemRecommendBinding,
private val onClickDelete: (Int) -> Unit
) :
RecyclerView.ViewHolder(binding.root) {
fun onBind(data: Recommend) {
binding.data = data
binding.ivItemRecommendDelete.setOnClickListener {
onClickDelete(data.recommendId)
}
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package com.sopt.peekabookaos.presentation.recommend

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.activityViewModels
import com.sopt.peekabookaos.R
import com.sopt.peekabookaos.databinding.DialogRecommendDeleteBinding
import com.sopt.peekabookaos.util.UiEvent
import com.sopt.peekabookaos.util.extensions.getSerializableCompat
import com.sopt.peekabookaos.util.extensions.repeatOnStarted
import timber.log.Timber

class RecommendDeleteDialog : DialogFragment() {
private var _binding: DialogRecommendDeleteBinding? = null
private val binding get() = _binding ?: error(getString(R.string.binding_error))

private val recommendViewModel by activityViewModels<RecommendViewModel>()

override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = DialogRecommendDeleteBinding.inflate(inflater, container, false)
return binding.root
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
isCancelable = true
initLayout()
initWarningDialogContent()
initConfirmBtnClickListener()
initCancelBtnClickListener()
collectUiEvent()
}

private fun collectUiEvent() {
repeatOnStarted {
recommendViewModel.uiEvent.collect { uiEvent ->
when (uiEvent) {
UiEvent.IDLE -> {}
UiEvent.SUCCESS -> {
recommendViewModel.getRecommend()
dismiss()
}

UiEvent.ERROR -> {
dismiss()
}
}
}
}
}

private fun initLayout() {
val ratio = 0.89
val layoutParams = requireNotNull(dialog).window!!.attributes
layoutParams.width = (resources.displayMetrics.widthPixels * ratio).toInt()
layoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT
requireNotNull(dialog).window!!.attributes = layoutParams
}

private fun initWarningDialogContent() {
val recommendType = arguments?.getSerializableCompat(RECOMMEND_TYPE) as? RecommendType
?: Timber.e(getString(R.string.null_point_exception_warning_dialog_argument))
with(binding) {
type = when (recommendType as RecommendType) {
RecommendType.RECOMMENDED ->
RecommendDeleteDialogContent().getRecommended(requireContext())

RecommendType.RECOMMENDING ->
RecommendDeleteDialogContent().getRecommending(requireContext())
}
}
}

private fun initConfirmBtnClickListener() {
binding.btnRecommendDeleteDialogConfirm.setOnClickListener {
recommendViewModel.deleteRecommend()
}
}

private fun initCancelBtnClickListener() {
binding.btnRecommendDeleteDialogCancel.setOnClickListener {
dismiss()
}
}

override fun onDestroyView() {
super.onDestroyView()
_binding = null
}

companion object {
const val DIALOG_TYPE = "recommendDeleteDialog"
const val RECOMMEND_TYPE = "recommendType"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.sopt.peekabookaos.presentation.recommend

import android.content.Context
import com.sopt.peekabookaos.R

data class RecommendDeleteDialogContent(
val title: String = "",
val content: String = ""
) {
fun getRecommended(context: Context): RecommendDeleteDialogContent =
RecommendDeleteDialogContent(
title = context.getString(R.string.recommend_recommended_delete_dialog_title),
content = context.getString(R.string.recommend_recommended_delete_dialog_content)
)

fun getRecommending(context: Context): RecommendDeleteDialogContent =
RecommendDeleteDialogContent(
title = context.getString(R.string.recommend_recommending_delete_dialog_title),
content = context.getString(R.string.recommend_recommending_delete_dialog_content)
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class RecommendFragment : BindingFragment<FragmentRecommendBinding>(R.layout.fra

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.viewModel = recommendViewModel
initAdapter()
initTabLayout()
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.sopt.peekabookaos.presentation.recommend

enum class RecommendType {
RECOMMENDED, RECOMMENDING
}
Original file line number Diff line number Diff line change
@@ -1,32 +1,97 @@
package com.sopt.peekabookaos.presentation.recommend

import androidx.lifecycle.LiveData
import androidx.lifecycle.MediatorLiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.sopt.peekabookaos.domain.entity.Recommend
import com.sopt.peekabookaos.domain.usecase.DeleteRecommendUseCase
import com.sopt.peekabookaos.domain.usecase.GetRecommendUseCase
import com.sopt.peekabookaos.util.UiEvent
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.launch
import timber.log.Timber
import javax.inject.Inject

@HiltViewModel
class RecommendViewModel @Inject constructor(
private val getRecommendUseCase: GetRecommendUseCase
private val getRecommendUseCase: GetRecommendUseCase,
private val deleteRecommendUseCase: DeleteRecommendUseCase
) : ViewModel() {
private val _uiEvent = MutableSharedFlow<UiEvent>()
val uiEvent = _uiEvent.asSharedFlow()

private val _recommendedBook = MutableLiveData<List<Recommend>>()
val recommendedBook: LiveData<List<Recommend>> = _recommendedBook

private val _recommendingBook = MutableLiveData<List<Recommend>>()
val recommendingBook: LiveData<List<Recommend>> = _recommendingBook

// private val _isEditMode = MutableLiveData(false)
// val isEditMode: LiveData<Boolean> = _isEditMode

private val _recommendId = MutableLiveData(-1)
val recommendId: LiveData<Int> = _recommendId

private val _isEditMode = MediatorLiveData<Boolean>().apply {
value = false

addSource(_recommendedBook) { value = checkEditMode(it, _recommendingBook.value) }
addSource(_recommendingBook) { value = checkEditMode(_recommendedBook.value, it) }
}
val isEditMode: LiveData<Boolean> = _isEditMode

private fun checkEditMode(
recommendedBooks: List<Recommend>?,
recommendingBooks: List<Recommend>?
): Boolean {
val recommendedBooksEmpty = recommendedBooks.isNullOrEmpty()
val recommendingBooksEmpty = recommendingBooks.isNullOrEmpty()

return if (recommendedBooksEmpty && recommendingBooksEmpty) false else _isEditMode.value
?: false
}

fun toggleEditMode() {
_isEditMode.value = _isEditMode.value?.not()
val updatedRecommendedBooks =
_recommendedBook.value?.map { it.copy(isEditMode = _isEditMode.value!!) }
_recommendedBook.value = updatedRecommendedBooks!!

val updatedRecommendingBooks =
_recommendingBook.value?.map { it.copy(isEditMode = _isEditMode.value!!) }
_recommendingBook.value = updatedRecommendingBooks!!
}

fun deleteRecommend() {
viewModelScope.launch {
_uiEvent.emit(UiEvent.IDLE)
deleteRecommendUseCase(requireNotNull(_recommendId.value))
.onSuccess {
_uiEvent.emit(UiEvent.SUCCESS)
}.onFailure { throwable ->
_uiEvent.emit(UiEvent.ERROR)
Timber.e("$throwable")
}
}
}

fun setRecommendId(recommendId: Int) {
_recommendId.value = recommendId
}

fun getRecommend() {
viewModelScope.launch {
getRecommendUseCase()
.onSuccess { response ->
_recommendingBook.value = response.recommendingBook
_recommendedBook.value = response.recommendedBook
_recommendingBook.value =
response.recommendingBook.map { it.copy(isEditMode = _isEditMode.value!!) }
_recommendedBook.value =
response.recommendedBook.map { it.copy(isEditMode = _isEditMode.value!!) }
checkEditMode(_recommendingBook.value, _recommendedBook.value)
}.onFailure { throwable ->
Timber.e("$throwable")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import androidx.fragment.app.activityViewModels
import com.sopt.peekabookaos.R
import com.sopt.peekabookaos.databinding.ItemRecommendViewBinding
import com.sopt.peekabookaos.util.binding.BindingFragment
import com.sopt.peekabookaos.util.extensions.withArgs
import dagger.hilt.android.AndroidEntryPoint

@AndroidEntryPoint
Expand All @@ -24,7 +25,17 @@ class RecommendedFragment :
}

private fun initAdapter() {
binding.rvRecommend.adapter = BookRecommendAdapter()
binding.rvRecommend.adapter = BookRecommendAdapter(::onClickDelete)
}

private fun onClickDelete(recommendId: Int) {
recommendViewModel.setRecommendId(recommendId)
RecommendDeleteDialog().withArgs {
putSerializable(
RecommendDeleteDialog.RECOMMEND_TYPE,
RecommendType.RECOMMENDED
)
}.show(childFragmentManager, RecommendDeleteDialog.DIALOG_TYPE)
}

private fun initObserver() {
Expand Down
Loading

0 comments on commit 0502928

Please sign in to comment.