diff --git a/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/common/layoutManager/CenterSmoothScroller.kt b/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/common/layoutManager/CenterSmoothScroller.kt new file mode 100644 index 000000000..7aa83f849 --- /dev/null +++ b/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/common/layoutManager/CenterSmoothScroller.kt @@ -0,0 +1,19 @@ +package com.emmsale.presentation.common.layoutManager + +import android.content.Context +import androidx.recyclerview.widget.LinearSmoothScroller +import dagger.hilt.android.qualifiers.ApplicationContext +import javax.inject.Inject + +class CenterSmoothScroller @Inject constructor( + @ApplicationContext context: Context, +) : LinearSmoothScroller(context) { + + override fun calculateDtToFit( + viewStart: Int, + viewEnd: Int, + boxStart: Int, + boxEnd: Int, + snapPreference: Int, + ): Int = (boxStart + (boxEnd - boxStart) / 2) - (viewStart + (viewEnd - viewStart) / 2) +} diff --git a/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/common/layoutManager/EndSmoothScroller.kt b/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/common/layoutManager/EndSmoothScroller.kt new file mode 100644 index 000000000..85118711b --- /dev/null +++ b/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/common/layoutManager/EndSmoothScroller.kt @@ -0,0 +1,13 @@ +package com.emmsale.presentation.common.layoutManager + +import android.content.Context +import androidx.recyclerview.widget.LinearSmoothScroller +import dagger.hilt.android.qualifiers.ApplicationContext +import javax.inject.Inject + +class EndSmoothScroller @Inject constructor( + @ApplicationContext context: Context, +) : LinearSmoothScroller(context) { + + override fun getVerticalSnapPreference(): Int = SNAP_TO_END +} diff --git a/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/common/views/SubTextInputWindow.kt b/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/common/views/SubTextInputWindow.kt index fd7cb1664..a50fa1b2a 100644 --- a/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/common/views/SubTextInputWindow.kt +++ b/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/common/views/SubTextInputWindow.kt @@ -46,6 +46,7 @@ class SubTextInputWindow @JvmOverloads constructor( init { applyStyledAttributes(attrs) addView(binding.root) + isClickable = true background = context.getColor(R.color.white).toDrawable() elevation = 5f.dp } diff --git a/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/childCommentList/ChildCommentsActivity.kt b/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/childCommentList/ChildCommentsActivity.kt index 533193ed0..a8b5770e5 100644 --- a/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/childCommentList/ChildCommentsActivity.kt +++ b/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/childCommentList/ChildCommentsActivity.kt @@ -7,6 +7,7 @@ import android.os.Bundle import androidx.activity.OnBackPressedCallback import androidx.activity.viewModels import androidx.core.view.isVisible +import androidx.lifecycle.lifecycleScope import com.emmsale.R import com.emmsale.data.model.Comment import com.emmsale.databinding.ActivityChildCommentsBinding @@ -14,6 +15,8 @@ import com.emmsale.presentation.base.NetworkActivity import com.emmsale.presentation.common.extension.hideKeyboard import com.emmsale.presentation.common.extension.showKeyboard import com.emmsale.presentation.common.extension.showSnackBar +import com.emmsale.presentation.common.layoutManager.CenterSmoothScroller +import com.emmsale.presentation.common.layoutManager.EndSmoothScroller import com.emmsale.presentation.common.recyclerView.DividerItemDecoration import com.emmsale.presentation.common.views.InfoDialog import com.emmsale.presentation.common.views.WarningDialog @@ -24,9 +27,11 @@ import com.emmsale.presentation.ui.childCommentList.ChildCommentsViewModel.Compa import com.emmsale.presentation.ui.childCommentList.recyclerView.CommentsAdapter import com.emmsale.presentation.ui.childCommentList.uiState.ChildCommentsUiEvent import com.emmsale.presentation.ui.feedDetail.FeedDetailActivity -import com.emmsale.presentation.ui.feedDetail.uiState.CommentsUiState import com.emmsale.presentation.ui.profile.ProfileActivity import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import javax.inject.Inject @AndroidEntryPoint class ChildCommentsActivity : @@ -34,8 +39,14 @@ class ChildCommentsActivity : override val viewModel: ChildCommentsViewModel by viewModels() + @Inject + lateinit var centerSmoothScroller: CenterSmoothScroller + + @Inject + lateinit var endSmoothScroller: EndSmoothScroller + private val commentsAdapter: CommentsAdapter = CommentsAdapter( - onCommentClick = { comment -> viewModel.unhighlight(comment.id) }, + onCommentClick = { }, onAuthorImageClick = { authorId -> ProfileActivity.startActivity(this, authorId) }, onCommentMenuClick = ::showCommentMenuDialog, ) @@ -63,9 +74,21 @@ class ChildCommentsActivity : private fun BottomMenuDialog.addCommentUpdateButton(commentId: Long) { addMenuItemBelow(context.getString(R.string.all_update_button_label)) { - viewModel.setEditMode(true, commentId) + viewModel.startEditComment(commentId) binding.stiwCommentUpdate.requestFocusOnEditText() showKeyboard() + startToEditComment(commentId) + } + } + + private fun startToEditComment(commentId: Long) { + val position = viewModel.comments.value.getPosition(commentId) + + lifecycleScope.launch { + delay(KEYBOARD_SHOW_WAITING_TIME) + binding.rvChildcommentsChildcomments + .layoutManager + ?.startSmoothScroll(endSmoothScroller.apply { targetPosition = position }) } } @@ -130,12 +153,12 @@ class ChildCommentsActivity : hideKeyboard() } binding.onCommentUpdateCancelButtonClick = { - viewModel.setEditMode(false) + viewModel.cancelEditComment() hideKeyboard() } binding.onUpdatedCommentSubmitButtonClick = { - val commentId = viewModel.editingCommentId.value - if (commentId != null) viewModel.updateComment(commentId, it) + val comment = viewModel.editingComment.value + if (comment != null) viewModel.updateComment(comment.id, it) hideKeyboard() } } @@ -172,23 +195,31 @@ class ChildCommentsActivity : private fun observeComments() { viewModel.comments.observe(this) { - commentsAdapter.submitList(it.commentUiStates) { scrollToIfFirstFetch(it) } + commentsAdapter.submitList(it.commentUiStates) { handleHighlightComment() } } } - private fun scrollToIfFirstFetch(commentUiState: CommentsUiState) { - fun cantScroll(): Boolean = - viewModel.isAlreadyFirstFetched || commentUiState.commentUiStates.isEmpty() + private fun handleHighlightComment() { + if (highlightCommentId == INVALID_COMMENT_ID || isNotRealFirstEnter()) return - if (highlightCommentId == INVALID_COMMENT_ID || cantScroll()) return - val position = viewModel.comments.value.commentUiStates - .indexOfFirst { - it.comment.id == highlightCommentId - } - binding.rvChildcommentsChildcomments.scrollToPosition(position) - - viewModel.highlight(highlightCommentId) viewModel.isAlreadyFirstFetched = true + viewModel.highlightCommentOnFirstEnter(highlightCommentId) + highlightCommentOnFirstEnter() + } + + private fun isNotRealFirstEnter(): Boolean = + viewModel.isAlreadyFirstFetched || viewModel.comments.value.commentUiStates.isEmpty() + + private fun highlightCommentOnFirstEnter() { + val position = viewModel.comments.value.getPosition(highlightCommentId) + + binding.rvChildcommentsChildcomments.scrollToPosition(position) + lifecycleScope.launch { + delay(100L) // 버그 때문에 + binding.rvChildcommentsChildcomments + .layoutManager + ?.startSmoothScroll(centerSmoothScroller.apply { targetPosition = position }) + } } private fun observeUiEvent() { @@ -242,6 +273,7 @@ class ChildCommentsActivity : private const val KEY_HIGHLIGHT_COMMENT_ID = "KEY_HIGHLIGHT_COMMENT_ID" private const val KEY_FROM_POST_DETAIL = "KEY_FROM_POST_DETAIL" private const val INVALID_COMMENT_ID: Long = -1 + private const val KEYBOARD_SHOW_WAITING_TIME = 300L fun startActivity( context: Context, diff --git a/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/childCommentList/ChildCommentsViewModel.kt b/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/childCommentList/ChildCommentsViewModel.kt index 023431030..d3eff880c 100644 --- a/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/childCommentList/ChildCommentsViewModel.kt +++ b/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/childCommentList/ChildCommentsViewModel.kt @@ -4,6 +4,8 @@ import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.map +import androidx.lifecycle.viewModelScope +import com.emmsale.data.model.Comment import com.emmsale.data.repository.interfaces.CommentRepository import com.emmsale.data.repository.interfaces.TokenRepository import com.emmsale.presentation.base.RefreshableViewModel @@ -14,6 +16,8 @@ import com.emmsale.presentation.ui.childCommentList.uiState.ChildCommentsUiEvent import com.emmsale.presentation.ui.feedDetail.uiState.CommentsUiState import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.Job +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch import javax.inject.Inject import kotlin.properties.Delegates.vetoable @@ -36,16 +40,16 @@ class ChildCommentsViewModel @Inject constructor( private val _comments = NotNullMutableLiveData(CommentsUiState()) val comments: NotNullLiveData = _comments - private val _editingCommentId = MutableLiveData() - val editingCommentId: LiveData = _editingCommentId + private val _editingComment = MutableLiveData() + val editingComment: LiveData = _editingComment - val editingCommentContent: LiveData = _editingCommentId.map { commentId -> - if (commentId == null) null else _comments.value[commentId]?.comment?.content - } + val isEditingComment: LiveData = _editingComment.map { it != null } private val _canSubmitComment = NotNullMutableLiveData(true) val canSubmitComment: NotNullLiveData = _canSubmitComment + private var unhighlightJob: Job? = null + private val _uiEvent = SingleLiveEvent() val uiEvent: LiveData = _uiEvent @@ -69,7 +73,7 @@ class ChildCommentsViewModel @Inject constructor( fun updateComment(commentId: Long, content: String): Job = commandAndRefresh( command = { commentRepository.updateComment(commentId, content) }, - onSuccess = { _editingCommentId.value = null }, + onSuccess = { _editingComment.value = null }, onFailure = { _, _ -> _uiEvent.value = ChildCommentsUiEvent.CommentUpdateFail }, onStart = { _canSubmitComment.value = false }, onFinish = { _canSubmitComment.value = true }, @@ -80,8 +84,26 @@ class ChildCommentsViewModel @Inject constructor( onFailure = { _, _ -> _uiEvent.value = ChildCommentsUiEvent.CommentDeleteFail }, ) - fun setEditMode(isEditMode: Boolean, commentId: Long = INVALID_COMMENT_ID) { - _editingCommentId.value = if (isEditMode) commentId else null + fun startEditComment(commentId: Long) { + _editingComment.value = comments.value.commentUiStates + .find { it.comment.id == commentId } + ?.comment + ?: return + unhighlightJob?.cancel() + _comments.value = _comments.value.highlight(commentId) + } + + fun cancelEditComment() { + _editingComment.value = null + _comments.value = _comments.value.unhighlight() + } + + fun highlightCommentOnFirstEnter(commentId: Long) { + _comments.value = _comments.value.highlight(commentId) + unhighlightJob = viewModelScope.launch { + delay(COMMENT_HIGHLIGHTING_DURATION_ON_FIRST_ENTER) + _comments.value = _comments.value.unhighlight() + } } fun reportComment(commentId: Long): Job = command( @@ -106,24 +128,12 @@ class ChildCommentsViewModel @Inject constructor( onSuccess = { _comments.value = CommentsUiState(uid, it) }, ) - fun highlight(commentId: Long) { - val comment = _comments.value.commentUiStates.find { it.comment.id == commentId } ?: return - if (comment.isHighlight) return - _comments.value = _comments.value.highlight(commentId) - } - - fun unhighlight(commentId: Long) { - val comment = _comments.value.commentUiStates.find { it.comment.id == commentId } ?: return - if (!comment.isHighlight) return - _comments.value = _comments.value.unhighlight() - } - companion object { const val KEY_FEED_ID = "KEY_FEED_ID" const val KEY_PARENT_COMMENT_ID = "KEY_PARENT_COMMENT_ID" - private const val INVALID_COMMENT_ID: Long = -1 - private const val REPORT_DUPLICATE_ERROR_CODE = 400 + + private const val COMMENT_HIGHLIGHTING_DURATION_ON_FIRST_ENTER = 2000L } } diff --git a/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/feedDetail/FeedDetailActivity.kt b/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/feedDetail/FeedDetailActivity.kt index 8308b5c0c..dff03f39b 100644 --- a/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/feedDetail/FeedDetailActivity.kt +++ b/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/feedDetail/FeedDetailActivity.kt @@ -5,7 +5,6 @@ import android.content.Intent import android.os.Bundle import androidx.activity.viewModels import androidx.lifecycle.lifecycleScope -import androidx.recyclerview.widget.LinearSmoothScroller import com.emmsale.R import com.emmsale.data.model.Comment import com.emmsale.databinding.ActivityFeedDetailBinding @@ -13,6 +12,8 @@ import com.emmsale.presentation.base.NetworkActivity import com.emmsale.presentation.common.extension.hideKeyboard import com.emmsale.presentation.common.extension.showKeyboard import com.emmsale.presentation.common.extension.showSnackBar +import com.emmsale.presentation.common.layoutManager.CenterSmoothScroller +import com.emmsale.presentation.common.layoutManager.EndSmoothScroller import com.emmsale.presentation.common.recyclerView.DividerItemDecoration import com.emmsale.presentation.common.views.InfoDialog import com.emmsale.presentation.common.views.WarningDialog @@ -26,6 +27,7 @@ import com.emmsale.presentation.ui.profile.ProfileActivity import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.delay import kotlinx.coroutines.launch +import javax.inject.Inject @AndroidEntryPoint class FeedDetailActivity : @@ -37,6 +39,12 @@ class FeedDetailActivity : override val viewModel: FeedDetailViewModel by viewModels() + @Inject + lateinit var centerSmoothScroller: CenterSmoothScroller + + @Inject + lateinit var endSmoothScroller: EndSmoothScroller + private val bottomMenuDialog: BottomMenuDialog by lazy { BottomMenuDialog(this) } private val feedAndCommentsAdapter = FeedAndCommentsAdapter( @@ -71,9 +79,21 @@ class FeedDetailActivity : private fun BottomMenuDialog.addCommentUpdateButton(commentId: Long) { addMenuItemBelow(context.getString(R.string.all_update_button_label)) { - viewModel.startEditComment(commentId) binding.stiwCommentUpdate.requestFocusOnEditText() showKeyboard() + viewModel.startEditComment(commentId) + startToEditComment(commentId) + } + } + + private fun startToEditComment(commentId: Long) { + val position = viewModel.feedDetailUiState.value.getCommentPosition(commentId) + + lifecycleScope.launch { + delay(KEYBOARD_SHOW_WAITING_TIME) + binding.rvFeedAndComments + .layoutManager + ?.startSmoothScroll(endSmoothScroller.apply { targetPosition = position }) } } @@ -142,8 +162,8 @@ class FeedDetailActivity : hideKeyboard() } binding.onUpdatedCommentSubmitButtonClick = { - val commentId = viewModel.editingCommentId.value - if (commentId != null) viewModel.updateComment(commentId, it) + val comment = viewModel.editingComment.value + if (comment != null) viewModel.updateComment(comment.id, it) hideKeyboard() } } @@ -212,17 +232,33 @@ class FeedDetailActivity : private fun observeFeedDetail() { viewModel.feedDetailUiState.observe(this) { val feedAndComments = listOf(it.feedUiState) + it.commentsUiState.commentUiStates - feedAndCommentsAdapter.submitList(feedAndComments) { - if (highlightCommentId == INVALID_COMMENT_ID || isNotRealFirstFetch()) return@submitList - viewModel.highlightComment(highlightCommentId) - viewModel.isAlreadyFirstFetched = true - } + feedAndCommentsAdapter.submitList(feedAndComments) { handleHighlightComment() } } } - private fun isNotRealFirstFetch(): Boolean = + private fun handleHighlightComment() { + if (highlightCommentId == INVALID_COMMENT_ID || isNotRealFirstEnter()) return + + viewModel.isAlreadyFirstFetched = true + viewModel.highlightCommentOnFirstEnter(highlightCommentId) + highlightCommentOnFirstEnter() + } + + private fun isNotRealFirstEnter(): Boolean = viewModel.isAlreadyFirstFetched || viewModel.commentUiStates.isEmpty() + private fun highlightCommentOnFirstEnter() { + val position = viewModel.feedDetailUiState.value.getCommentPosition(highlightCommentId) + + binding.rvFeedAndComments.scrollToPosition(position) + lifecycleScope.launch { + delay(100L) // 버그 때문에 + binding.rvFeedAndComments + .layoutManager + ?.startSmoothScroll(centerSmoothScroller.apply { targetPosition = position }) + } + } + private fun observeUiEvent() { viewModel.uiEvent.observe(this, ::handleUiEvent) } @@ -274,8 +310,6 @@ class FeedDetailActivity : binding.btiwCommentPost.clearText() scrollToLastPosition() } - - is FeedDetailUiEvent.CommentHighlight -> highlightComment(uiEvent.commentId) } } @@ -284,26 +318,10 @@ class FeedDetailActivity : binding.rvFeedAndComments.smoothScrollToPosition(commentsCount) } - private fun highlightComment(commentId: Long) { - val position = viewModel.commentUiStates - .indexOfFirst { - it.comment.id == commentId - } - - binding.rvFeedAndComments.scrollToPosition(position + 1) - lifecycleScope.launch { - delay(200L) - binding.rvFeedAndComments.layoutManager?.startSmoothScroll( - object : LinearSmoothScroller(this@FeedDetailActivity) { - override fun getVerticalSnapPreference(): Int = SNAP_TO_START - }.apply { targetPosition = position + 1 }, - ) - } - } - companion object { private const val KEY_HIGHLIGHT_COMMENT_ID = "KEY_HIGHLIGHT_COMMENT_ID" private const val INVALID_COMMENT_ID: Long = -1 + private const val KEYBOARD_SHOW_WAITING_TIME = 300L fun startActivity( context: Context, diff --git a/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/feedDetail/FeedDetailViewModel.kt b/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/feedDetail/FeedDetailViewModel.kt index 255506ea8..b21267f2f 100644 --- a/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/feedDetail/FeedDetailViewModel.kt +++ b/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/feedDetail/FeedDetailViewModel.kt @@ -28,6 +28,7 @@ import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.Job import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll +import kotlinx.coroutines.delay import kotlinx.coroutines.launch import javax.inject.Inject import kotlin.properties.Delegates @@ -60,15 +61,16 @@ class FeedDetailViewModel @Inject constructor( val isFeedDetailWrittenByLoginUser: Boolean get() = feed.writer.id == uid - private val _editingCommentId = MutableLiveData() - val editingCommentId: LiveData = _editingCommentId - val editingCommentContent: LiveData = _editingCommentId.map { commentId -> - if (commentId == null) null else commentUiStates.find { it.comment.id == commentId }?.comment?.content - } + private val _editingComment = MutableLiveData() + val editingComment: LiveData = _editingComment + + val isEditingComment: LiveData = _editingComment.map { it != null } private val _canSubmitComment = NotNullMutableLiveData(true) val canSubmitComment: NotNullLiveData = _canSubmitComment + private var unhighlightJob: Job? = null + private val _uiEvent = SingleLiveEvent() val uiEvent: LiveData = _uiEvent @@ -185,7 +187,7 @@ class FeedDetailViewModel @Inject constructor( fun updateComment(commentId: Long, content: String): Job = commandAndRefresh( command = { commentRepository.updateComment(commentId, content) }, - onSuccess = { _editingCommentId.value = null }, + onSuccess = { _editingComment.value = null }, onFailure = { _, _ -> _uiEvent.value = FeedDetailUiEvent.CommentUpdateFail }, onStart = { _canSubmitComment.value = false }, onFinish = { _canSubmitComment.value = true }, @@ -197,13 +199,25 @@ class FeedDetailViewModel @Inject constructor( ) fun startEditComment(commentId: Long) { - _editingCommentId.value = commentId - highlightComment(commentId) + _editingComment.value = commentUiStates + .find { it.comment.id == commentId } + ?.comment + ?: return + unhighlightJob?.cancel() + _feedDetailUiState.value = _feedDetailUiState.value.highlightComment(commentId) } fun cancelEditComment() { - _editingCommentId.value = null - unhighlightComment() + _editingComment.value = null + _feedDetailUiState.value = _feedDetailUiState.value.unhighlightComment() + } + + fun highlightCommentOnFirstEnter(commentId: Long) { + _feedDetailUiState.value = _feedDetailUiState.value.highlightComment(commentId) + unhighlightJob = viewModelScope.launch { + delay(COMMENT_HIGHLIGHTING_DURATION_ON_FIRST_ENTER) + _feedDetailUiState.value = _feedDetailUiState.value.unhighlightComment() + } } fun reportComment(commentId: Long): Job = command( @@ -223,20 +237,13 @@ class FeedDetailViewModel @Inject constructor( }, ) - fun highlightComment(commentId: Long) { - _feedDetailUiState.value = _feedDetailUiState.value.highlightComment(commentId) - _uiEvent.value = FeedDetailUiEvent.CommentHighlight(commentId) - } - - private fun unhighlightComment() { - _feedDetailUiState.value = _feedDetailUiState.value.unhighlightComment() - } - companion object { const val KEY_FEED_ID: String = "KEY_FEED_ID" private const val DEFAULT_FEED_ID: Long = -1 private const val DELETED_FEED_FETCH_ERROR_CODE = 403 private const val REPORT_DUPLICATE_ERROR_CODE = 400 + + private const val COMMENT_HIGHLIGHTING_DURATION_ON_FIRST_ENTER = 2000L } } diff --git a/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/feedDetail/uiState/CommentUiState.kt b/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/feedDetail/uiState/CommentUiState.kt index d51e2f19e..e2c379680 100644 --- a/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/feedDetail/uiState/CommentUiState.kt +++ b/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/feedDetail/uiState/CommentUiState.kt @@ -8,6 +8,12 @@ data class CommentUiState( val comment: Comment, ) : FeedOrCommentUiState { + constructor(uid: Long, comment: Comment, isHighlight: Boolean = false) : this( + isWrittenByLoginUser = uid == comment.writer.id, + isHighlight = isHighlight, + comment = comment, + ) + override val id: Long = comment.id override val viewType: Int = VIEW_TYPE @@ -22,12 +28,5 @@ data class CommentUiState( companion object { const val VIEW_TYPE = 1 - - fun create(uid: Long, comment: Comment, isHighlight: Boolean = false): CommentUiState = - CommentUiState( - isWrittenByLoginUser = uid == comment.writer.id, - isHighlight = isHighlight, - comment = comment, - ) } } diff --git a/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/feedDetail/uiState/CommentsUiState.kt b/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/feedDetail/uiState/CommentsUiState.kt index 320f4ba95..05598bd40 100644 --- a/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/feedDetail/uiState/CommentsUiState.kt +++ b/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/feedDetail/uiState/CommentsUiState.kt @@ -6,16 +6,14 @@ data class CommentsUiState(val commentUiStates: List = emptyList constructor(uid: Long, comments: List) : this( comments.flatMap { comment -> - listOf(CommentUiState.create(uid, comment)) + - comment.childComments.map { childComment -> - CommentUiState.create(uid, childComment) - } + listOf(CommentUiState(uid, comment)) + + comment.childComments.map { childComment -> CommentUiState(uid, childComment) } }, ) constructor(uid: Long, comment: Comment) : this( - listOf(CommentUiState.create(uid, comment)) + - comment.childComments.map { childComment -> CommentUiState.create(uid, childComment) }, + listOf(CommentUiState(uid, comment)) + + comment.childComments.map { childComment -> CommentUiState(uid, childComment) }, ) val size: Int = commentUiStates.size @@ -23,6 +21,9 @@ data class CommentsUiState(val commentUiStates: List = emptyList operator fun get(commentId: Long): CommentUiState? = commentUiStates.find { it.comment.id == commentId } + fun getPosition(commentId: Long): Int = + commentUiStates.indexOfFirst { it.comment.id == commentId } + fun highlight(commentId: Long) = copy( commentUiStates = commentUiStates.map { if (it.comment.id == commentId) it.highlight() else it.unhighlight() }, ) diff --git a/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/feedDetail/uiState/FeedDetailUiEvent.kt b/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/feedDetail/uiState/FeedDetailUiEvent.kt index a25ff414b..5194643c2 100644 --- a/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/feedDetail/uiState/FeedDetailUiEvent.kt +++ b/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/feedDetail/uiState/FeedDetailUiEvent.kt @@ -11,5 +11,4 @@ sealed interface FeedDetailUiEvent { object CommentReportFail : FeedDetailUiEvent object CommentReportComplete : FeedDetailUiEvent object CommentPostComplete : FeedDetailUiEvent - data class CommentHighlight(val commentId: Long) : FeedDetailUiEvent } diff --git a/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/feedDetail/uiState/FeedDetailUiState.kt b/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/feedDetail/uiState/FeedDetailUiState.kt index 5d8beb5c9..f4b29aadf 100644 --- a/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/feedDetail/uiState/FeedDetailUiState.kt +++ b/android/2023-emmsale/app/src/main/java/com/emmsale/presentation/ui/feedDetail/uiState/FeedDetailUiState.kt @@ -12,6 +12,8 @@ data class FeedDetailUiState( commentsUiState = CommentsUiState(uid, comments), ) + fun getCommentPosition(commentId: Long): Int = commentsUiState.getPosition(commentId) + 1 + fun highlightComment(commentId: Long): FeedDetailUiState = copy(commentsUiState = commentsUiState.highlight(commentId)) diff --git a/android/2023-emmsale/app/src/main/res/layout/activity_child_comments.xml b/android/2023-emmsale/app/src/main/res/layout/activity_child_comments.xml index e33fcdc78..519f72838 100644 --- a/android/2023-emmsale/app/src/main/res/layout/activity_child_comments.xml +++ b/android/2023-emmsale/app/src/main/res/layout/activity_child_comments.xml @@ -52,69 +52,66 @@ app:swipeRefreshColor="@{@color/primary_color}" android:layout_width="0dp" android:layout_height="0dp" - app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintBottom_toTopOf="@id/barrier_input_windows" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/tb_childcomments_toolbar"> - - - - - - - - - - - + + + + + + + + @@ -74,6 +74,7 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent" app:hint="@string/comments_edit_comment_hint" + app:visible="@{!vm.isEditingComment}" app:onSubmit="@{(content) -> onCommentSubmitButtonClick.invoke(content)}" app:isSubmitEnabled="@{vm.canSubmitComment}" app:submitButtonLabel="@string/comments_comment_submit_button_label" /> @@ -85,14 +86,21 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintBottom_toBottomOf="parent" - app:text="@{vm.editingCommentContent}" - app:visible="@{vm.editingCommentId != null}" + app:text="@{vm.editingComment.content}" + app:visible="@{vm.isEditingComment}" app:isSubmitEnabled="@{vm.canSubmitComment}" app:submitButtonLabel="@string/all_update_button_label" app:cancelButtonLabel="@string/all_cancel" app:onCancel="@{() -> onCommentUpdateCancelButtonClick.invoke()}" app:onSubmit="@{(content) -> onUpdatedCommentSubmitButtonClick.invoke(content)}" /> + +