fix(comment): 답글 더보기 Bottom Sheet 적용 및 삭제/신고 API 연동
답글 리스트에서 PopupMenu를 Bottom Sheet로 통일하고, 내 답글은 삭제, 타인 답글은 신고 메뉴만 노출하도록 변경. 삭제는 원 댓글 삭제와 동일한 API(deleteComment)를 사용하며, 신고는 reportComment로 연동.
This commit is contained in:
		@@ -1,10 +1,8 @@
 | 
			
		||||
package kr.co.vividnext.sodalive.chat.character.comment
 | 
			
		||||
 | 
			
		||||
import android.content.Context
 | 
			
		||||
import android.view.LayoutInflater
 | 
			
		||||
import android.view.View
 | 
			
		||||
import android.view.ViewGroup
 | 
			
		||||
import android.widget.PopupMenu
 | 
			
		||||
import androidx.recyclerview.widget.RecyclerView
 | 
			
		||||
import androidx.viewbinding.ViewBinding
 | 
			
		||||
import coil.load
 | 
			
		||||
@@ -15,8 +13,7 @@ import kr.co.vividnext.sodalive.databinding.ItemCharacterCommentReplyBinding
 | 
			
		||||
 | 
			
		||||
class CharacterCommentReplyAdapter(
 | 
			
		||||
    private val currentUserId: Long,
 | 
			
		||||
    private val onModify: (id: Long, newText: String, isReply: Boolean) -> Unit,
 | 
			
		||||
    private val onDelete: (id: Long, isReply: Boolean) -> Unit
 | 
			
		||||
    private val onMore: (data: CharacterReplyResponse, isOwner: Boolean) -> Unit
 | 
			
		||||
) : RecyclerView.Adapter<CharacterReplyVH>() {
 | 
			
		||||
 | 
			
		||||
    // 첫 번째 아이템은 항상 원본 댓글
 | 
			
		||||
@@ -27,15 +24,21 @@ class CharacterCommentReplyAdapter(
 | 
			
		||||
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CharacterReplyVH {
 | 
			
		||||
        return if (viewType == 0) {
 | 
			
		||||
            CharacterReplyHeaderVH(
 | 
			
		||||
                ItemCharacterCommentBinding.inflate(LayoutInflater.from(parent.context), parent, false)
 | 
			
		||||
                ItemCharacterCommentBinding.inflate(
 | 
			
		||||
                    LayoutInflater.from(parent.context),
 | 
			
		||||
                    parent,
 | 
			
		||||
                    false
 | 
			
		||||
                )
 | 
			
		||||
            )
 | 
			
		||||
        } else {
 | 
			
		||||
            CharacterReplyItemVH(
 | 
			
		||||
                context = parent.context,
 | 
			
		||||
                binding = ItemCharacterCommentReplyBinding.inflate(LayoutInflater.from(parent.context), parent, false),
 | 
			
		||||
                binding = ItemCharacterCommentReplyBinding.inflate(
 | 
			
		||||
                    LayoutInflater.from(parent.context),
 | 
			
		||||
                    parent,
 | 
			
		||||
                    false
 | 
			
		||||
                ),
 | 
			
		||||
                currentUserId = currentUserId,
 | 
			
		||||
                onModify = onModify,
 | 
			
		||||
                onDelete = onDelete
 | 
			
		||||
                onMoreCallback = onMore
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -76,11 +79,9 @@ class CharacterReplyHeaderVH(
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class CharacterReplyItemVH(
 | 
			
		||||
    private val context: Context,
 | 
			
		||||
    private val binding: ItemCharacterCommentReplyBinding,
 | 
			
		||||
    private val currentUserId: Long,
 | 
			
		||||
    private val onModify: (id: Long, newText: String, isReply: Boolean) -> Unit,
 | 
			
		||||
    private val onDelete: (id: Long, isReply: Boolean) -> Unit
 | 
			
		||||
    private val onMoreCallback: (data: CharacterReplyResponse, isOwner: Boolean) -> Unit
 | 
			
		||||
) : CharacterReplyVH(binding) {
 | 
			
		||||
 | 
			
		||||
    override fun bind(item: Any) {
 | 
			
		||||
@@ -103,25 +104,8 @@ class CharacterReplyItemVH(
 | 
			
		||||
        val isOwner = data.memberId == currentUserId
 | 
			
		||||
        binding.ivMenu.visibility = View.VISIBLE
 | 
			
		||||
        binding.ivMenu.setOnClickListener {
 | 
			
		||||
            val popup = PopupMenu(context, it)
 | 
			
		||||
            if (isOwner) {
 | 
			
		||||
                popup.menuInflater.inflate(R.menu.content_comment_option_menu, popup.menu)
 | 
			
		||||
            } else {
 | 
			
		||||
                popup.menuInflater.inflate(R.menu.content_comment_option_menu2, popup.menu)
 | 
			
		||||
            }
 | 
			
		||||
            popup.setOnMenuItemClickListener { mi ->
 | 
			
		||||
                when (mi.itemId) {
 | 
			
		||||
                    R.id.menu_review_modify -> {
 | 
			
		||||
                        // 간단화: 수정은 현재 텍스트 그대로 콜백 (실서비스는 인라인 에디트 UI 구성)
 | 
			
		||||
                        onModify(data.replyId, data.comment, true)
 | 
			
		||||
                    }
 | 
			
		||||
                    R.id.menu_review_delete -> {
 | 
			
		||||
                        onDelete(data.replyId, true)
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                true
 | 
			
		||||
            }
 | 
			
		||||
            popup.show()
 | 
			
		||||
            // 답글의 더보기는 PopupMenu 대신 BottomSheet를 사용하기 위해 Fragment 측 콜백으로 위임
 | 
			
		||||
            onMoreCallback(data, isOwner)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,7 @@ import android.view.View
 | 
			
		||||
import android.view.ViewGroup
 | 
			
		||||
import android.view.inputmethod.InputMethodManager
 | 
			
		||||
import android.widget.Toast
 | 
			
		||||
import androidx.appcompat.app.AlertDialog
 | 
			
		||||
import androidx.recyclerview.widget.LinearLayoutManager
 | 
			
		||||
import androidx.recyclerview.widget.RecyclerView
 | 
			
		||||
import coil.load
 | 
			
		||||
@@ -140,20 +141,89 @@ class CharacterCommentReplyFragment : BaseFragment<FragmentCharacterCommentReply
 | 
			
		||||
 | 
			
		||||
        adapter = CharacterCommentReplyAdapter(
 | 
			
		||||
            currentUserId = SharedPreferenceManager.userId,
 | 
			
		||||
            onModify = { id, newText, isReply ->
 | 
			
		||||
                Toast.makeText(requireContext(), "수정 스텁: $id", Toast.LENGTH_SHORT).show()
 | 
			
		||||
            },
 | 
			
		||||
            onDelete = { id, isReply ->
 | 
			
		||||
                val index = adapter.items.indexOfFirst {
 | 
			
		||||
                    when (it) {
 | 
			
		||||
                        is CharacterReplyResponse -> it.replyId == id
 | 
			
		||||
                        else -> false
 | 
			
		||||
            onMore = { reply, isOwner ->
 | 
			
		||||
                CharacterCommentMoreBottomSheet.newInstance(isOwner).apply {
 | 
			
		||||
                    onReport = {
 | 
			
		||||
                        val reportSheet = CharacterCommentReportBottomSheet.newInstance()
 | 
			
		||||
                        reportSheet.onSubmit = { reason ->
 | 
			
		||||
                            val token = "Bearer ${SharedPreferenceManager.token}"
 | 
			
		||||
                            val d = repository.reportComment(
 | 
			
		||||
                                characterId = characterId,
 | 
			
		||||
                                commentId = reply.replyId,
 | 
			
		||||
                                reason = reason,
 | 
			
		||||
                                token = token
 | 
			
		||||
                            ).subscribeOn(Schedulers.io())
 | 
			
		||||
                                .observeOn(AndroidSchedulers.mainThread())
 | 
			
		||||
                                .subscribe({ resp ->
 | 
			
		||||
                                    if (resp.success) {
 | 
			
		||||
                                        Toast.makeText(
 | 
			
		||||
                                            requireContext(),
 | 
			
		||||
                                            "신고가 접수되었습니다.",
 | 
			
		||||
                                            Toast.LENGTH_SHORT
 | 
			
		||||
                                        ).show()
 | 
			
		||||
                                    } else {
 | 
			
		||||
                                        Toast.makeText(
 | 
			
		||||
                                            requireContext(),
 | 
			
		||||
                                            resp.message ?: "요청 중 오류가 발생했습니다",
 | 
			
		||||
                                            Toast.LENGTH_SHORT
 | 
			
		||||
                                        ).show()
 | 
			
		||||
                                    }
 | 
			
		||||
                                }, { e ->
 | 
			
		||||
                                    Toast.makeText(
 | 
			
		||||
                                        requireContext(),
 | 
			
		||||
                                        e.message ?: "요청 중 오류가 발생했습니다",
 | 
			
		||||
                                        Toast.LENGTH_SHORT
 | 
			
		||||
                                    ).show()
 | 
			
		||||
                                })
 | 
			
		||||
                            compositeDisposable.add(d)
 | 
			
		||||
                        }
 | 
			
		||||
                if (index > 0) { // 0은 원본 댓글이므로 제외
 | 
			
		||||
                        reportSheet.show(parentFragmentManager, "reply_report")
 | 
			
		||||
                    }
 | 
			
		||||
                    onDelete = {
 | 
			
		||||
                        AlertDialog.Builder(requireContext())
 | 
			
		||||
                            .setTitle(getString(R.string.confirm_delete_title))
 | 
			
		||||
                            .setMessage(getString(R.string.confirm_delete_message))
 | 
			
		||||
                            .setPositiveButton(getString(R.string.confirm)) { _, _ ->
 | 
			
		||||
                                val token = "Bearer ${SharedPreferenceManager.token}"
 | 
			
		||||
                                loadingDialog.show(screenWidth)
 | 
			
		||||
                                val d = repository.deleteComment(
 | 
			
		||||
                                    characterId = characterId,
 | 
			
		||||
                                    commentId = reply.replyId,
 | 
			
		||||
                                    token = token
 | 
			
		||||
                                ).subscribeOn(Schedulers.io())
 | 
			
		||||
                                    .observeOn(AndroidSchedulers.mainThread())
 | 
			
		||||
                                    .doFinally { loadingDialog.dismiss() }
 | 
			
		||||
                                    .subscribe({ resp ->
 | 
			
		||||
                                        if (resp.success) {
 | 
			
		||||
                                            val index = adapter.items
 | 
			
		||||
                                                .indexOfFirst {
 | 
			
		||||
                                                    it is CharacterReplyResponse
 | 
			
		||||
                                                        && it.replyId == reply.replyId
 | 
			
		||||
                                                }
 | 
			
		||||
                                            if (index > 0) {
 | 
			
		||||
                                                adapter.items.removeAt(index)
 | 
			
		||||
                                                adapter.notifyItemRemoved(index)
 | 
			
		||||
                                            }
 | 
			
		||||
                                        } else {
 | 
			
		||||
                                            Toast.makeText(
 | 
			
		||||
                                                requireActivity(),
 | 
			
		||||
                                                resp.message ?: "요청 중 오류가 발생했습니다",
 | 
			
		||||
                                                Toast.LENGTH_SHORT
 | 
			
		||||
                                            ).show()
 | 
			
		||||
                                        }
 | 
			
		||||
                                    }, { e ->
 | 
			
		||||
                                        Toast.makeText(
 | 
			
		||||
                                            requireActivity(),
 | 
			
		||||
                                            e.message ?: "요청 중 오류가 발생했습니다",
 | 
			
		||||
                                            Toast.LENGTH_SHORT
 | 
			
		||||
                                        ).show()
 | 
			
		||||
                                    })
 | 
			
		||||
                                compositeDisposable.add(d)
 | 
			
		||||
                            }
 | 
			
		||||
                            .setNegativeButton(getString(R.string.cancel), null)
 | 
			
		||||
                            .show()
 | 
			
		||||
                    }
 | 
			
		||||
                }.show(childFragmentManager, "reply_more")
 | 
			
		||||
            }
 | 
			
		||||
        ).apply {
 | 
			
		||||
            items.clear()
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user