feat(creator-channel): 댓글 수정 입력 모드를 구현한다
This commit is contained in:
@@ -4,11 +4,9 @@ import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.text.InputType
|
||||
import android.text.method.ScrollingMovementMethod
|
||||
import android.widget.EditText
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.widget.doAfterTextChanged
|
||||
@@ -22,6 +20,7 @@ import kr.co.vividnext.sodalive.explorer.profile.creator_community.all.player.Cr
|
||||
import kr.co.vividnext.sodalive.explorer.profile.creator_community.all.player.CreatorCommunityMediaPlayerManager
|
||||
import kr.co.vividnext.sodalive.extensions.loadUrl
|
||||
import kr.co.vividnext.sodalive.extensions.moneyFormat
|
||||
import kr.co.vividnext.sodalive.v2.components.modal.V2ModalDialog
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.community.detail.reply.CreatorChannelCommunityReplyActivity
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.community.detail.ui.CreatorChannelCommunityCommentAdapter
|
||||
import org.koin.androidx.viewmodel.ext.android.viewModel
|
||||
@@ -39,11 +38,12 @@ class CreatorChannelCommunityDetailActivity : BaseActivity<ActivityCreatorChanne
|
||||
}
|
||||
private val commentAdapter = CreatorChannelCommunityCommentAdapter(
|
||||
onReplyClick = { comment -> replyLauncher.launch(CreatorChannelCommunityReplyActivity.newIntent(this, postId, comment)) },
|
||||
onModifyClick = { comment -> showModifyCommentDialog(comment.commentId, comment.comment) },
|
||||
onModifyClick = { comment -> viewModel.startCommentEdit(comment.commentId, comment.comment) },
|
||||
onDeleteClick = { comment -> showDeleteCommentDialog(comment.commentId) }
|
||||
)
|
||||
private lateinit var mediaPlayerManager: CreatorCommunityMediaPlayerManager
|
||||
private var postId: Long = 0L
|
||||
private var renderedEditingCommentId: Long? = null
|
||||
|
||||
override fun setupView() {
|
||||
mediaPlayerManager = CreatorCommunityMediaPlayerManager(this) { }
|
||||
@@ -70,18 +70,24 @@ class CreatorChannelCommunityDetailActivity : BaseActivity<ActivityCreatorChanne
|
||||
viewModel.updateCommentInput(editable?.toString().orEmpty())
|
||||
}
|
||||
binding.btnCreatorChannelCommunityDetailSend.setOnClickListener { viewModel.submitComment() }
|
||||
binding.btnCreatorChannelCommunityDetailEditCancel.setOnClickListener { viewModel.cancelCommentEdit() }
|
||||
binding.ivCreatorChannelCommunityDetailHeart.setOnClickListener { viewModel.toggleLike() }
|
||||
|
||||
viewModel.detailStateLiveData.observe(this) { state -> render(state) }
|
||||
viewModel.commentWrittenEventLiveData.observe(this) { written ->
|
||||
if (written == true) {
|
||||
setResult(Activity.RESULT_OK)
|
||||
hideCommentKeyboard()
|
||||
viewModel.consumeCommentWrittenEvent()
|
||||
}
|
||||
}
|
||||
viewModel.postChangedEventLiveData.observe(this) { changed ->
|
||||
if (changed == true) {
|
||||
setResult(Activity.RESULT_OK)
|
||||
val current = viewModel.detailStateLiveData.value as? CreatorChannelCommunityDetailUiState.Content
|
||||
if (current?.editingCommentId != null) {
|
||||
hideCommentKeyboard()
|
||||
}
|
||||
viewModel.consumePostChangedEvent()
|
||||
}
|
||||
}
|
||||
@@ -171,6 +177,11 @@ class CreatorChannelCommunityDetailActivity : BaseActivity<ActivityCreatorChanne
|
||||
etCreatorChannelCommunityDetailComment.setText(content.commentInput)
|
||||
etCreatorChannelCommunityDetailComment.setSelection(content.commentInput.length)
|
||||
}
|
||||
btnCreatorChannelCommunityDetailEditCancel.isVisible = content.editingCommentId != null
|
||||
if (content.editingCommentId != null && content.editingCommentId != renderedEditingCommentId) {
|
||||
showCommentKeyboard()
|
||||
}
|
||||
renderedEditingCommentId = content.editingCommentId
|
||||
updateSendButton(content.commentInput.trim().isNotEmpty() && !content.isSendingComment)
|
||||
}
|
||||
|
||||
@@ -191,35 +202,32 @@ class CreatorChannelCommunityDetailActivity : BaseActivity<ActivityCreatorChanne
|
||||
mediaPlayerManager.toggleContent(CreatorCommunityContentItem(post.postId, audioUrl))
|
||||
}
|
||||
|
||||
private fun showModifyCommentDialog(commentId: Long, comment: String) {
|
||||
val editText = EditText(this).apply {
|
||||
inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_FLAG_MULTI_LINE
|
||||
minLines = 3
|
||||
setText(comment)
|
||||
setSelection(comment.length)
|
||||
}
|
||||
AlertDialog.Builder(this)
|
||||
.setTitle(R.string.menu_modify_action)
|
||||
.setView(editText)
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.setPositiveButton(R.string.confirm) { _, _ ->
|
||||
val modifiedComment = editText.text?.toString().orEmpty()
|
||||
if (modifiedComment.trim() == comment.trim()) {
|
||||
Toast.makeText(this, R.string.audio_content_comment_no_change, Toast.LENGTH_SHORT).show()
|
||||
} else {
|
||||
viewModel.modifyComment(commentId, modifiedComment)
|
||||
}
|
||||
}
|
||||
.show()
|
||||
private fun showDeleteCommentDialog(commentId: Long) {
|
||||
V2ModalDialog(
|
||||
activity = this,
|
||||
layoutInflater = layoutInflater,
|
||||
title = getString(R.string.confirm_delete_title),
|
||||
desc = getString(R.string.confirm_delete_message),
|
||||
confirmButtonTitle = getString(R.string.confirm),
|
||||
confirmButtonClick = { viewModel.deleteComment(commentId) },
|
||||
cancelButtonTitle = getString(R.string.cancel)
|
||||
).show(screenWidth)
|
||||
}
|
||||
|
||||
private fun showDeleteCommentDialog(commentId: Long) {
|
||||
AlertDialog.Builder(this)
|
||||
.setTitle(R.string.confirm_delete_title)
|
||||
.setMessage(R.string.confirm_delete_message)
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.setPositiveButton(R.string.confirm) { _, _ -> viewModel.deleteComment(commentId) }
|
||||
.show()
|
||||
private fun showCommentKeyboard() {
|
||||
val editText = binding.etCreatorChannelCommunityDetailComment
|
||||
editText.requestFocus()
|
||||
editText.post {
|
||||
val inputMethodManager = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||
inputMethodManager.showSoftInput(editText, InputMethodManager.SHOW_IMPLICIT)
|
||||
}
|
||||
}
|
||||
|
||||
private fun hideCommentKeyboard() {
|
||||
val editText = binding.etCreatorChannelCommunityDetailComment
|
||||
val inputMethodManager = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||
inputMethodManager.hideSoftInputFromWindow(editText.windowToken, 0)
|
||||
editText.clearFocus()
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
@@ -128,6 +128,11 @@ class CreatorChannelCommunityDetailViewModel(
|
||||
fun submitComment() {
|
||||
val content = _detailStateLiveData.value as? CreatorChannelCommunityDetailUiState.Content ?: return
|
||||
val comment = content.commentInput.trim()
|
||||
val editingCommentId = content.editingCommentId
|
||||
if (editingCommentId != null) {
|
||||
submitCommentEdit(content, editingCommentId, comment)
|
||||
return
|
||||
}
|
||||
if (!content.isCommentAvailable || comment.isBlank() || isSendingComment) return
|
||||
|
||||
isSendingComment = true
|
||||
@@ -175,6 +180,26 @@ class CreatorChannelCommunityDetailViewModel(
|
||||
_postChangedEventLiveData.value = false
|
||||
}
|
||||
|
||||
fun startCommentEdit(commentId: Long, comment: String) {
|
||||
val content = _detailStateLiveData.value as? CreatorChannelCommunityDetailUiState.Content ?: return
|
||||
if (commentId <= 0) return
|
||||
|
||||
_detailStateLiveData.value = content.copy(
|
||||
commentInput = comment,
|
||||
editingCommentId = commentId,
|
||||
editingOriginalComment = comment
|
||||
)
|
||||
}
|
||||
|
||||
fun cancelCommentEdit() {
|
||||
val content = _detailStateLiveData.value as? CreatorChannelCommunityDetailUiState.Content ?: return
|
||||
_detailStateLiveData.value = content.copy(
|
||||
commentInput = "",
|
||||
editingCommentId = null,
|
||||
editingOriginalComment = ""
|
||||
)
|
||||
}
|
||||
|
||||
fun modifyComment(commentId: Long, comment: String) {
|
||||
val trimmedComment = comment.trim()
|
||||
if (commentId <= 0 || isModifyingComment) return
|
||||
@@ -192,6 +217,57 @@ class CreatorChannelCommunityDetailViewModel(
|
||||
modifyComment(ModifyCommentRequest(commentId = commentId, isActive = false))
|
||||
}
|
||||
|
||||
private fun submitCommentEdit(
|
||||
content: CreatorChannelCommunityDetailUiState.Content,
|
||||
commentId: Long,
|
||||
comment: String
|
||||
) {
|
||||
if (commentId <= 0 || isModifyingComment) return
|
||||
if (comment.isBlank()) {
|
||||
showBlankCommentToast()
|
||||
return
|
||||
}
|
||||
if (comment == content.editingOriginalComment.trim()) {
|
||||
showNoChangeToast()
|
||||
return
|
||||
}
|
||||
|
||||
isModifyingComment = true
|
||||
_detailStateLiveData.value = content.copy(isSendingComment = true)
|
||||
compositeDisposable.add(
|
||||
legacyRepository.modifyComment(ModifyCommentRequest(commentId = commentId, comment = comment), authToken())
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
{ response ->
|
||||
isModifyingComment = false
|
||||
val current = _detailStateLiveData.value as? CreatorChannelCommunityDetailUiState.Content
|
||||
?: return@subscribe
|
||||
if (response.success) {
|
||||
_postChangedEventLiveData.value = true
|
||||
_detailStateLiveData.value = current.copy(
|
||||
commentInput = "",
|
||||
isSendingComment = false,
|
||||
editingCommentId = null,
|
||||
editingOriginalComment = ""
|
||||
)
|
||||
loadDetail(postId)
|
||||
} else {
|
||||
showMutationFailureToast(response.message)
|
||||
_detailStateLiveData.value = current.copy(isSendingComment = false)
|
||||
}
|
||||
},
|
||||
{
|
||||
isModifyingComment = false
|
||||
val current = _detailStateLiveData.value as? CreatorChannelCommunityDetailUiState.Content
|
||||
?: return@subscribe
|
||||
showMutationFailureToast()
|
||||
_detailStateLiveData.value = current.copy(isSendingComment = false)
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private fun handleDetailResponse(response: ApiResponse<CreatorChannelCommunityPostDetailResponse>) {
|
||||
val data = response.data
|
||||
if (!response.success || data == null) {
|
||||
@@ -313,6 +389,12 @@ class CreatorChannelCommunityDetailViewModel(
|
||||
)
|
||||
}
|
||||
|
||||
private fun showNoChangeToast() {
|
||||
_toastLiveData.value = CreatorChannelEvent(
|
||||
ToastMessage(resId = R.string.audio_content_comment_no_change)
|
||||
)
|
||||
}
|
||||
|
||||
private fun CreatorChannelCommunityPostDetailResponse.toUiModel() = CreatorChannelCommunityPostDetailUiModel(
|
||||
postId = postId,
|
||||
creatorId = creatorId,
|
||||
@@ -392,7 +474,9 @@ sealed interface CreatorChannelCommunityDetailUiState {
|
||||
val hasNextComment: Boolean,
|
||||
val isLoadingComments: Boolean = false,
|
||||
val commentInput: String = "",
|
||||
val isSendingComment: Boolean = false
|
||||
val isSendingComment: Boolean = false,
|
||||
val editingCommentId: Long? = null,
|
||||
val editingOriginalComment: String = ""
|
||||
) : CreatorChannelCommunityDetailUiState
|
||||
}
|
||||
|
||||
|
||||
@@ -25,18 +25,6 @@
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_creator_channel_community_detail_title"
|
||||
style="@style/Typography.Body1"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:includeFontPadding="false"
|
||||
android:text="@string/creator_channel_community_detail_title"
|
||||
android:textColor="@color/white"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
@@ -335,5 +323,16 @@
|
||||
android:enabled="false"
|
||||
android:src="@drawable/ic_new_arrow_up_gray"
|
||||
tools:background="@drawable/bg_creator_channel_community_send_enabled" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/btn_creator_channel_community_detail_edit_cancel"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_marginStart="@dimen/spacing_8"
|
||||
android:background="@android:color/transparent"
|
||||
android:contentDescription="@string/cancel"
|
||||
android:src="@drawable/ic_x_white"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible" />
|
||||
</LinearLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
Reference in New Issue
Block a user