Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature/#105] : 대댓글 UI, API #122

Merged
merged 21 commits into from
Nov 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
73cad21
#105 [UI] : set up child comment ui
sohyun127 Nov 22, 2024
b60d1b2
#105 [FEAT] : set up child comment get api
sohyun127 Nov 22, 2024
05fa397
#105 [FEAT] : set up comment multi view type adapter
sohyun127 Nov 22, 2024
e8849ff
#105 [FEAT] : combine comment and child comment like
sohyun127 Nov 22, 2024
c008967
#105 [FEAT] : add snack bar type
sohyun127 Nov 22, 2024
0d32c51
#105 [FEAT] : set up comment post api v3
sohyun127 Nov 23, 2024
15a20ea
#105 [FEAT] : add show keyboard extension
sohyun127 Nov 23, 2024
195119f
#105 [FEAT] : add comment type
sohyun127 Nov 23, 2024
d2254ae
#105 [FEAT] : set up comment, child comment icon click event / post c…
sohyun127 Nov 23, 2024
7ff9b68
#105 [UI] : change child comment label padding
sohyun127 Nov 23, 2024
fb046b2
#105 [FEAT] : set up home detail comment ban
sohyun127 Nov 23, 2024
56b5ed3
#105 [FEAT] : set up placeholder after child comment posting
sohyun127 Nov 23, 2024
387b67c
Merge branch 'develop' of https://github.com/Team-Wable/WABLE-ANDROID…
sohyun127 Nov 24, 2024
4b7a441
Merge branch 'develop' of https://github.com/Team-Wable/WABLE-ANDROID…
sohyun127 Nov 25, 2024
2f0a3c5
#105 [FIX] : fix after child comment posting logic
sohyun127 Nov 25, 2024
7220f26
#105 [FIX] : fix comment update
sohyun127 Nov 25, 2024
cce8e52
#105 [FEAT] : set up comment loading progress
sohyun127 Nov 25, 2024
539c85a
#105 [UI] : change comment edit text cursor
sohyun127 Nov 25, 2024
883afa8
#105 [UI] : change posting cursor
sohyun127 Nov 25, 2024
5c8be04
#105 [FEAT] : set write child comment btn visibility
sohyun127 Nov 25, 2024
72903b1
Merge branch 'develop' of https://github.com/Team-Wable/WABLE-ANDROID…
sohyun127 Nov 25, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ package com.teamwable.data.mapper.toData

import com.teamwable.network.dto.request.RequestPostCommentDto

internal fun Pair<String, String>.toPostCommentDto(): RequestPostCommentDto =
RequestPostCommentDto(first, second)
internal fun Triple<String, Long, Long>.toPostCommentDto(): RequestPostCommentDto =
RequestPostCommentDto(commentText = first, parentCommentId = second, parentCommentWriterId = third)
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,18 @@ internal fun ResponseCommentDto.toComment(): Comment =
this.time,
this.memberFanTeam,
this.contentId,
this.isBlind ?: false,
this.isBlind,
this.parentCommentId,
)

internal fun ResponseCommentDto.toComments(): List<Comment> {
val comments = mutableListOf<Comment>()

comments.add(this.toComment())

this.childComments?.forEach { child ->
comments.addAll(child.toComments())
}

return comments
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ interface CommentRepository {

suspend fun deleteComment(commentId: Long): Result<Unit>

suspend fun postComment(contentId: Long, commentText: String): Result<Unit>
suspend fun postComment(contentId: Long, commentInfo: Triple<String, Long, Long>): Result<Unit>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3 :
image
세븐틴까지 가주세요

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Zzzzzzzzㅋㅋㅋㅋㅋㅋㅋㅋㅋ


suspend fun postGhost(request: Ghost): Result<Unit>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ package com.teamwable.data.repositoryimpl
import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.PagingData
import androidx.paging.flatMap
import androidx.paging.map
import com.teamwable.data.mapper.toData.toPostCommentDto
import com.teamwable.data.mapper.toData.toPostCommentLikeDto
import com.teamwable.data.mapper.toData.toPostGhostDto
import com.teamwable.data.mapper.toModel.toComment
import com.teamwable.data.mapper.toModel.toComments
import com.teamwable.data.repository.CommentRepository
import com.teamwable.model.Comment
import com.teamwable.model.Ghost
Expand All @@ -28,7 +30,7 @@ internal class DefaultCommentRepository @Inject constructor(
getNextCursor = { comments -> comments.lastOrNull()?.commentId },
)
}.flow.map { pagingData ->
pagingData.map { it.toComment() }
pagingData.flatMap { it.toComments() }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3 : flatMap 이런 것도 잇군요? 내장 코드 활용 넘 잘하는 ㄷㄷ

}
}

Expand All @@ -50,9 +52,8 @@ internal class DefaultCommentRepository @Inject constructor(
return it.handleThrowable()
}

override suspend fun postComment(contentId: Long, commentText: String): Result<Unit> = runCatching {
val request = Pair(commentText, "comment").toPostCommentDto()
apiService.postComment(contentId, request)
override suspend fun postComment(contentId: Long, commentInfo: Triple<String, Long, Long>): Result<Unit> = runCatching {
apiService.postComment(contentId, commentInfo.toPostCommentDto())
Unit
}.onFailure {
return it.handleThrowable()
Expand Down
1 change: 1 addition & 0 deletions core/model/src/main/java/com/teamwable/model/Comment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ data class Comment(
val postAuthorTeamTag: String,
val feedId: Long?,
val isBlind: Boolean,
val parentCommentId: Long?,
val ghostColor: String = GhostColor.DEFAULT_0,
val isAuth: Boolean = false,
)
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import retrofit2.http.Path
import retrofit2.http.Query

interface CommentService {
@GET("api/v2/content/{contentId}/comments")
@GET("api/v3/content/{contentId}/comments")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이거 v3생기면 추가하기로 한거 아니였나요?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

어떤걸 추가하기로 했죠??!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

앗 이거 저희 쪽에서 v3로 새로 함수 만들어서 쓴다고 착각했네요

근데 지금 로그아웃 하고 로그인이 안되서
이전 버전(v1) 으로 dto포함 로그인 돌리면 되긴합니다만
내일 서버와 이야기 해보아야 할것 같아요!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아하!! 로그인 api v2로 업하고 체크를 못했네요 서버랑 이야기 해볼게요!

suspend fun getHomeDetailComments(
@Path(value = "contentId") contentId: Long,
@Query(value = "cursor") cursor: Long = -1,
Expand All @@ -31,7 +31,7 @@ interface CommentService {
@Path(value = "commentId") commentId: Long,
): BaseUnitResponse<Unit>

@POST("api/v1/content/{contentId}/comment")
@POST("api/v3/content/{contentId}/comment")
suspend fun postComment(
@Path(value = "contentId") contentId: Long,
@Body request: RequestPostCommentDto,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ import kotlinx.serialization.Serializable
@Serializable
data class RequestPostCommentDto(
@SerialName("commentText") val commentText: String,
@SerialName("notificationTriggerType") val notificationTriggerType: String,
@SerialName("parentCommentId") val parentCommentId: Long,
@SerialName("parentCommentWriterId") val parentCommentWriterId: Long,
)
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,7 @@ data class ResponseCommentDto(
@SerialName("commentImageUrl") val commentImageUrl: String? = null,
@SerialName("memberFanTeam") val memberFanTeam: String,
@SerialName("contentId") val contentId: Long? = null,
@SerialName("isBlind") val isBlind: Boolean? = null, // TODO::임시로 nullable 받음 대댓글 구현후 non-null 변경 예정
@SerialName("isBlind") val isBlind: Boolean,
@SerialName("parentCommentId") val parentCommentId: Long? = null, // -1 : 부모 댓글, null : profile의 comment, 이외 : 대댓글
@SerialName("childComments") val childComments: List<ResponseCommentDto>? = null, // null : 대댓글, [] : 부모 댓글의 대댓글 없음
)
4 changes: 2 additions & 2 deletions core/ui/src/main/java/com/teamwable/ui/component/Snackbar.kt
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,8 @@ class Snackbar(private val view: View, private val type: SnackbarType) {
}, duration)
}

fun updateToCommentComplete() {
initLayout(SnackbarType.COMMENT_COMPLETE)
fun updateToCommentComplete(type: SnackbarType) {
initLayout(type)
dismissSnackbar(1000)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import android.util.TypedValue
import android.view.View
import android.view.WindowManager
import android.view.inputmethod.InputMethodManager
import android.widget.EditText
import android.widget.Toast
import androidx.annotation.ColorRes
import androidx.annotation.DrawableRes
Expand Down Expand Up @@ -53,6 +54,12 @@ fun Context.hideKeyboard(view: View) {
inputMethodManager.hideSoftInputFromWindow(view.windowToken, 0)
}

fun Context.showKeyboard(view: EditText) {
view.requestFocus()
val inputMethodManager = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
inputMethodManager.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT)
}

fun Context.openKeyboard(view: View) {
val inputMethodManager = getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager
inputMethodManager.showSoftInput(view, 0)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package com.teamwable.ui.shareAdapter

import android.graphics.Color
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.teamwable.model.Comment
import com.teamwable.ui.databinding.ItemChildCommentBinding
import com.teamwable.ui.extensions.load
import com.teamwable.ui.extensions.visible

class ChildCommentViewHolder private constructor(
private val binding: ItemChildCommentBinding,
commentClickListener: CommentClickListener,
) : RecyclerView.ViewHolder(binding.root), LikeableViewHolder {
private lateinit var item: Comment
override val likeBtn = binding.btnChildCommentLike
override val likeCountTv = binding.tvChildCommentLikeCount

init {
setupClickListener(itemView, binding.tvChildCommentContent) { commentClickListener.onItemClick(item.feedId ?: return@setupClickListener) }
setupClickListener(binding.btnChildCommentGhost) { commentClickListener.onGhostBtnClick(item.postAuthorId, item.commentId) }
setupClickListener(binding.btnChildCommentLike) { commentClickListener.onLikeBtnClick(this, item) }
setupClickListener(binding.ivChildCommentProfileImg, binding.tvChildCommentNickname) { commentClickListener.onPostAuthorProfileClick(item.postAuthorId) }
setupClickListener(binding.btnChildCommentMore) { commentClickListener.onKebabBtnClick(item) }
}

private fun setupClickListener(vararg views: View, action: () -> Unit) {
views.forEach { view ->
view.setOnClickListener {
if (this::item.isInitialized) action()
}
}
}

fun bind(comment: Comment?) {
item = comment ?: return
with(binding) {
ivChildCommentProfileImg.load(comment.postAuthorProfile)
tvChildCommentNickname.text = comment.postAuthorNickname
tvChildCommentGhostLevel.text = comment.postAuthorGhost
tvChildCommentUploadTime.text = comment.uploadTime
tvChildCommentContent.text = comment.content
btnChildCommentLike.isChecked = comment.isLiked
tvChildCommentLikeCount.text = comment.likedNumber
tvTeamTag.teamName = comment.postAuthorTeamTag
btnChildCommentGhost.isEnabled = !comment.isPostAuthorGhost
viewChildCommentTransparentBg.setBackgroundColor(Color.parseColor(comment.ghostColor))
btnChildCommentGhost.visible(!comment.isAuth)
spacerChildComment.visible(!comment.isAuth)
setBlindVisible(comment.isBlind)
}
}

private fun setBlindVisible(isBlind: Boolean) = with(binding) {
tvChildCommentContent.visible(!isBlind)
tvChildCommentBlind.visible(isBlind)
}

companion object {
fun from(parent: ViewGroup, commentClickListener: CommentClickListener): ChildCommentViewHolder =
ChildCommentViewHolder(
ItemChildCommentBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false,
),
commentClickListener,
)
}
}
Original file line number Diff line number Diff line change
@@ -1,26 +1,37 @@
package com.teamwable.ui.shareAdapter

import android.view.ViewGroup
import androidx.paging.PagingData
import androidx.paging.PagingDataAdapter
import androidx.recyclerview.widget.RecyclerView.ViewHolder
import com.teamwable.model.Comment
import com.teamwable.ui.extensions.ItemDiffCallback
import com.teamwable.ui.type.CommentType

class CommentAdapter(private val commentClickListener: CommentClickListener) : PagingDataAdapter<Comment, CommentViewHolder>(commentDiffCallback) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CommentViewHolder = CommentViewHolder.from(parent, commentClickListener)
class CommentAdapter(private val commentClickListener: CommentClickListener) : PagingDataAdapter<Comment, ViewHolder>(commentDiffCallback) {
override fun getItemViewType(position: Int): Int {
return when (getItem(position)?.parentCommentId ?: PARENT_COMMENT_DEFAULT) {
PARENT_COMMENT_DEFAULT -> CommentType.PARENT.id
else -> CommentType.CHILD.id
}
}

override fun onBindViewHolder(holder: CommentViewHolder, position: Int) = holder.bind(getItem(position))
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return when (viewType) {
CommentType.PARENT.id -> CommentViewHolder.from(parent, commentClickListener)
else -> ChildCommentViewHolder.from(parent, commentClickListener)
}
}

suspend fun removeComment(commentId: Long) {
val currentList = snapshot().items.toMutableList()
val indexToRemove = currentList.indexOfFirst { it.commentId == commentId }
if (indexToRemove != -1) {
currentList.removeAt(indexToRemove)
submitData(PagingData.from(currentList))
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val data = getItem(position)
when (data?.parentCommentId ?: PARENT_COMMENT_DEFAULT) {
PARENT_COMMENT_DEFAULT -> (holder as? CommentViewHolder)?.run { bind(data) }
else -> (holder as? ChildCommentViewHolder)?.run { bind(data) }
}
}

companion object {
const val PARENT_COMMENT_DEFAULT = -1L
private val commentDiffCallback = ItemDiffCallback<Comment>(
onItemsTheSame = { old, new -> old.commentId == new.commentId },
onContentsTheSame = { old, new -> old == new },
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,24 @@
package com.teamwable.ui.shareAdapter

import android.widget.CheckBox
import android.widget.TextView
import com.teamwable.model.Comment

interface CommentClickListener {
fun onGhostBtnClick(postAuthorId: Long, commentId: Long)

fun onLikeBtnClick(viewHolder: CommentViewHolder, comment: Comment)
fun onLikeBtnClick(viewHolder: LikeableViewHolder, comment: Comment)

fun onPostAuthorProfileClick(id: Long)

fun onKebabBtnClick(comment: Comment)

fun onItemClick(feedId: Long)

fun onChildCommentClick(comment: Comment)
}

interface LikeableViewHolder {
val likeBtn: CheckBox
val likeCountTv: TextView
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,18 @@ import com.teamwable.ui.extensions.visible
class CommentViewHolder private constructor(
private val binding: ItemCommentBinding,
commentClickListener: CommentClickListener,
) : RecyclerView.ViewHolder(binding.root) {
) : RecyclerView.ViewHolder(binding.root), LikeableViewHolder {
private lateinit var item: Comment
val likeBtn = binding.btnCommentLike
val likeCountTv = binding.tvCommentLikeCount
override val likeBtn = binding.btnCommentLike
override val likeCountTv = binding.tvCommentLikeCount

init {
setupClickListener(itemView, binding.tvCommentContent) { commentClickListener.onItemClick(item.feedId ?: return@setupClickListener) }
setupClickListener(binding.btnCommentGhost) { commentClickListener.onGhostBtnClick(item.postAuthorId, item.commentId) }
setupClickListener(binding.btnCommentLike) { commentClickListener.onLikeBtnClick(this, item) }
setupClickListener(binding.ivCommentProfileImg, binding.tvCommentNickname) { commentClickListener.onPostAuthorProfileClick(item.postAuthorId) }
setupClickListener(binding.btnCommentMore) { commentClickListener.onKebabBtnClick(item) }
setupClickListener(binding.btnCommentWriteChildComment, binding.tvCommentWriteChildCommentLabel) { commentClickListener.onChildCommentClick(item) }
}

private fun setupClickListener(vararg views: View, action: () -> Unit) {
Expand All @@ -50,6 +51,7 @@ class CommentViewHolder private constructor(
btnCommentGhost.visible(!comment.isAuth)
spacerComment.visible(!comment.isAuth)
setBlindVisible(comment.isBlind)
groupCommentWriteChildComment.visible(comment.parentCommentId != null)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,5 @@ interface FeedClickListener {

fun onKebabBtnClick(feed: Feed)

fun onCommentBtnClick(feedId: Long)
fun onCommentBtnClick(postAuthorNickname: String)
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class FeedViewHolder private constructor(
setupClickListener(binding.ivFeedProfileImg, binding.tvFeedNickname) { feedClickListener.onPostAuthorProfileClick(item.postAuthorId) }
setupClickListener(binding.ivFeedImg) { feedClickListener.onFeedImageClick(item.image) }
setupClickListener(binding.btnFeedMore) { feedClickListener.onKebabBtnClick(item) }
setupClickListener(binding.btnFeedComment) { feedClickListener.onCommentBtnClick(item.postAuthorNickname) }
}

private fun setupClickListener(vararg views: View, action: () -> Unit) {
Expand Down
6 changes: 6 additions & 0 deletions core/ui/src/main/java/com/teamwable/ui/type/CommentType.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.teamwable.ui.type

enum class CommentType(val id: Int) {
PARENT(0),
CHILD(1),
}
2 changes: 2 additions & 0 deletions core/ui/src/main/java/com/teamwable/ui/type/SnackbarType.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ enum class SnackbarType(
GHOST(R.string.label_snack_bar_ghost),
COMMENT_ING(R.string.label_snack_bar_comment_ing),
COMMENT_COMPLETE(R.string.label_snack_bar_comment_complete),
CHILD_COMMENT_ING(R.string.label_snack_bar_child_comment_ing),
CHILD_COMMENT_COMPLETE(R.string.label_snack_bar_child_comment_complete),
REPORT(R.string.label_snack_bar_report),
BAN(R.string.label_snack_bar_ban),
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import com.teamwable.model.LikeState
import com.teamwable.ui.component.BottomSheet
import com.teamwable.ui.component.TwoButtonDialog
import com.teamwable.ui.component.TwoLabelBottomSheet
import com.teamwable.ui.shareAdapter.CommentViewHolder
import com.teamwable.ui.shareAdapter.LikeableViewHolder
import com.teamwable.ui.type.BanTriggerType
import com.teamwable.ui.type.BottomSheetType
import com.teamwable.ui.type.DialogType
Expand Down Expand Up @@ -70,7 +70,7 @@ class CommentActionHandler(
}
}

fun onLikeBtnClick(viewHolder: CommentViewHolder, id: Long, saveLike: (Long, LikeState) -> Unit) {
fun onLikeBtnClick(viewHolder: LikeableViewHolder, id: Long, saveLike: (Long, LikeState) -> Unit) {
val likeCount = viewHolder.likeCountTv.text.toString().toInt()
val updatedLikeCount = if (viewHolder.likeBtn.isChecked) {
trackEvent(CLICK_LIKE_COMMENT)
Expand Down
1 change: 1 addition & 0 deletions core/ui/src/main/res/anim/spinner_rotate.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/iv_share_loading_spinner"
android:duration="1000"
android:fromDegrees="0"
android:interpolator="@android:interpolator/linear"
Expand Down
7 changes: 7 additions & 0 deletions core/ui/src/main/res/drawable/shape_purple_50_cursor.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<size android:width="2dp" />
<solid android:color="@color/purple_50" />
<corners android:radius="8dp" />
</shape>
Loading