feat: 커뮤니티 댓글

- 유료 커뮤니티 구매시 비밀 댓글 쓰기 기능 추가
This commit is contained in:
2025-06-13 16:52:40 +09:00
parent 09a2a96596
commit 28388497b8
11 changed files with 139 additions and 42 deletions

View File

@@ -35,8 +35,8 @@ android {
applicationId "kr.co.vividnext.sodalive" applicationId "kr.co.vividnext.sodalive"
minSdk 23 minSdk 23
targetSdk 34 targetSdk 34
versionCode 170 versionCode 171
versionName "1.37.3" versionName "1.37.2"
} }
buildTypes { buildTypes {

View File

@@ -88,6 +88,7 @@ object Constants {
const val EXTRA_COMMUNITY_POST_ID = "community_post_id" const val EXTRA_COMMUNITY_POST_ID = "community_post_id"
const val EXTRA_COMMUNITY_CREATOR_ID = "community_creator_id" const val EXTRA_COMMUNITY_CREATOR_ID = "community_creator_id"
const val EXTRA_COMMUNITY_POST_COMMENT = "community_post_comment_id" const val EXTRA_COMMUNITY_POST_COMMENT = "community_post_comment_id"
const val EXTRA_COMMUNITY_EXIST_ORDERED = "community_exist_ordered"
const val EXTRA_ALARM_ID = "alarm_id" const val EXTRA_ALARM_ID = "alarm_id"
const val EXTRA_ROULETTE_AVAILABLE_ACTIVE = "roulette_available_active" const val EXTRA_ROULETTE_AVAILABLE_ACTIVE = "roulette_available_active"

View File

@@ -51,12 +51,14 @@ class CreatorCommunityRepository(private val api: CreatorCommunityApi) {
postId: Long, postId: Long,
comment: String, comment: String,
parentId: Long? = null, parentId: Long? = null,
isSecret: Boolean = false,
token: String token: String
) = api.createCommunityPostComment( ) = api.createCommunityPostComment(
request = CreateCommunityPostCommentRequest( request = CreateCommunityPostCommentRequest(
comment = comment, comment = comment,
postId = postId, postId = postId,
parentId = parentId parentId = parentId,
isSecret = isSecret
), ),
authHeader = token authHeader = token
) )
@@ -66,7 +68,8 @@ class CreatorCommunityRepository(private val api: CreatorCommunityApi) {
authHeader = token authHeader = token
) )
fun getCommentReplyList(commentId: Long, page: Int, size: Int, token: String) = api.getCommentReplyList( fun getCommentReplyList(commentId: Long, page: Int, size: Int, token: String) =
api.getCommentReplyList(
commentId = commentId, commentId = commentId,
page = page, page = page,
size = size, size = size,

View File

@@ -4,7 +4,10 @@ import android.annotation.SuppressLint
import android.content.Intent import android.content.Intent
import android.graphics.Rect import android.graphics.Rect
import android.os.Bundle import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.view.View import android.view.View
import android.view.inputmethod.InputMethodManager
import android.widget.Toast import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
@@ -29,8 +32,10 @@ class CreatorCommunityAllActivity : BaseActivity<ActivityCreatorCommunityAllBind
private lateinit var loadingDialog: LoadingDialog private lateinit var loadingDialog: LoadingDialog
private lateinit var adapter: CreatorCommunityAllAdapter private lateinit var adapter: CreatorCommunityAllAdapter
private lateinit var mediaPlayerManager: CreatorCommunityMediaPlayerManager private lateinit var mediaPlayerManager: CreatorCommunityMediaPlayerManager
private lateinit var imm: InputMethodManager
private var creatorId: Long = 0 private var creatorId: Long = 0
private val handler = Handler(Looper.getMainLooper())
private val modifyResult = registerForActivityResult( private val modifyResult = registerForActivityResult(
ActivityResultContracts.StartActivityForResult() ActivityResultContracts.StartActivityForResult()
@@ -74,23 +79,28 @@ class CreatorCommunityAllActivity : BaseActivity<ActivityCreatorCommunityAllBind
override fun setupView() { override fun setupView() {
loadingDialog = LoadingDialog(this, layoutInflater) loadingDialog = LoadingDialog(this, layoutInflater)
imm = getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager
binding.toolbar.tvBack.text = "커뮤니티" binding.toolbar.tvBack.text = "커뮤니티"
binding.toolbar.tvBack.setOnClickListener { finish() } binding.toolbar.tvBack.setOnClickListener { finish() }
adapter = CreatorCommunityAllAdapter( adapter = CreatorCommunityAllAdapter(
screenWidth = screenWidth, screenWidth = screenWidth,
onClickLike = { viewModel.communityPostLike(it) }, onClickLike = { viewModel.communityPostLike(it) },
writeComment = { postId, parentId, comment -> writeComment = { postId, parentId, comment, isSecret ->
hideKeyboard()
viewModel.registerComment( viewModel.registerComment(
comment, comment,
postId, postId,
parentId parentId,
isSecret
) )
}, },
showCommentBottomSheetDialog = { showCommentBottomSheetDialog = { postId, existOrdered ->
val dialog = CreatorCommunityCommentFragment( val dialog = CreatorCommunityCommentFragment(
creatorId = creatorId, creatorId = creatorId,
postId = it postId = postId,
existOrdered = existOrdered
) )
dialog.show( dialog.show(
supportFragmentManager, supportFragmentManager,
@@ -223,4 +233,13 @@ class CreatorCommunityAllActivity : BaseActivity<ActivityCreatorCommunityAllBind
adapter.notifyDataSetChanged() adapter.notifyDataSetChanged()
} }
} }
private fun hideKeyboard() {
handler.postDelayed({
imm.hideSoftInputFromWindow(
window.decorView.applicationWindowToken,
InputMethodManager.HIDE_NOT_ALWAYS
)
}, 100)
}
} }

View File

@@ -31,8 +31,8 @@ import java.util.regex.Pattern
class CreatorCommunityAllAdapter( class CreatorCommunityAllAdapter(
private val screenWidth: Int, private val screenWidth: Int,
private val onClickLike: (Long) -> Unit, private val onClickLike: (Long) -> Unit,
private val writeComment: (Long, Long?, String) -> Unit, private val writeComment: (Long, Long?, String, Boolean) -> Unit,
private val showCommentBottomSheetDialog: (Long) -> Unit, private val showCommentBottomSheetDialog: (Long, Boolean) -> Unit,
private val onClickModify: (Long) -> Unit, private val onClickModify: (Long) -> Unit,
private val onClickDelete: (Long) -> Unit, private val onClickDelete: (Long) -> Unit,
private val onClickReport: (Long) -> Unit, private val onClickReport: (Long) -> Unit,
@@ -133,7 +133,8 @@ class CreatorCommunityAllAdapter(
item.postId, item.postId,
item.commentCount, item.commentCount,
item.isCommentAvailable, item.isCommentAvailable,
comment = item.firstComment comment = item.firstComment,
existOrdered = item.existOrdered
) )
} }
@@ -154,7 +155,8 @@ class CreatorCommunityAllAdapter(
postId: Long, postId: Long,
commentCount: Int, commentCount: Int,
isCommentAvailable: Boolean, isCommentAvailable: Boolean,
comment: GetCommunityPostCommentListItem? comment: GetCommunityPostCommentListItem?,
existOrdered: Boolean
) { ) {
if (isCommentAvailable) { if (isCommentAvailable) {
binding.llComment.visibility = View.VISIBLE binding.llComment.visibility = View.VISIBLE
@@ -172,8 +174,14 @@ class CreatorCommunityAllAdapter(
binding.tvCommentText.text = comment.comment binding.tvCommentText.text = comment.comment
binding.tvCommentText.visibility = View.VISIBLE binding.tvCommentText.visibility = View.VISIBLE
binding.rlInputComment.visibility = View.GONE binding.rlInputComment.visibility = View.GONE
binding.tvSecret.visibility = View.GONE
binding.llComment.setOnClickListener { showCommentBottomSheetDialog(postId) } binding.llComment.setOnClickListener {
showCommentBottomSheetDialog(
postId,
existOrdered
)
}
} else { } else {
binding.tvCommentText.visibility = View.GONE binding.tvCommentText.visibility = View.GONE
binding.rlInputComment.visibility = View.VISIBLE binding.rlInputComment.visibility = View.VISIBLE
@@ -185,8 +193,18 @@ class CreatorCommunityAllAdapter(
binding.ivCommentSend.setOnClickListener { binding.ivCommentSend.setOnClickListener {
val inputComment = binding.etComment.text.toString() val inputComment = binding.etComment.text.toString()
val isSecret = binding.tvSecret.isSelected
binding.etComment.setText("") binding.etComment.setText("")
writeComment(postId, null, inputComment) writeComment(postId, null, inputComment, isSecret)
}
if (existOrdered) {
binding.tvSecret.visibility = View.VISIBLE
binding.tvSecret.setOnClickListener {
binding.tvSecret.isSelected = !binding.tvSecret.isSelected
}
} else {
binding.tvSecret.visibility = View.GONE
} }
binding.llComment.setOnClickListener {} binding.llComment.setOnClickListener {}

View File

@@ -144,7 +144,12 @@ class CreatorCommunityAllViewModel(
} }
fun registerComment(comment: String, postId: Long, parentId: Long? = null) { fun registerComment(
comment: String,
postId: Long,
parentId: Long? = null,
isSecret: Boolean = false
) {
if (!_isLoading.value!!) { if (!_isLoading.value!!) {
_isLoading.value = true _isLoading.value = true
} }
@@ -154,6 +159,7 @@ class CreatorCommunityAllViewModel(
postId = postId, postId = postId,
comment = comment, comment = comment,
parentId = parentId, parentId = parentId,
isSecret = isSecret,
token = "Bearer ${SharedPreferenceManager.token}" token = "Bearer ${SharedPreferenceManager.token}"
) )
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())

View File

@@ -7,5 +7,6 @@ import com.google.gson.annotations.SerializedName
data class CreateCommunityPostCommentRequest( data class CreateCommunityPostCommentRequest(
@SerializedName("comment") val comment: String, @SerializedName("comment") val comment: String,
@SerializedName("postId") val postId: Long, @SerializedName("postId") val postId: Long,
@SerializedName("parentId") val parentId: Long? @SerializedName("parentId") val parentId: Long?,
@SerializedName("isSecret") val isSecret: Boolean = false
) )

View File

@@ -15,7 +15,8 @@ import kr.co.vividnext.sodalive.explorer.profile.creator_community.GetCommunityP
class CreatorCommunityCommentFragment( class CreatorCommunityCommentFragment(
private val creatorId: Long, private val creatorId: Long,
private val postId: Long private val postId: Long,
private val existOrdered: Boolean
) : BottomSheetDialogFragment() { ) : BottomSheetDialogFragment() {
private lateinit var binding: DialogAudioContentCommentBinding private lateinit var binding: DialogAudioContentCommentBinding
@@ -51,7 +52,8 @@ class CreatorCommunityCommentFragment(
val commentListFragmentTag = "COMMENT_LIST_FRAGMENT" val commentListFragmentTag = "COMMENT_LIST_FRAGMENT"
val commentListFragment = CreatorCommunityCommentListFragment.newInstance( val commentListFragment = CreatorCommunityCommentListFragment.newInstance(
creatorId = creatorId, creatorId = creatorId,
postId = postId postId = postId,
existOrdered = existOrdered
) )
val fragmentTransaction = childFragmentManager.beginTransaction() val fragmentTransaction = childFragmentManager.beginTransaction()
fragmentTransaction.add(R.id.fl_container, commentListFragment, commentListFragmentTag) fragmentTransaction.add(R.id.fl_container, commentListFragment, commentListFragmentTag)

View File

@@ -35,6 +35,7 @@ class CreatorCommunityCommentListFragment : BaseFragment<FragmentAudioContentCom
private var creatorId: Long = 0 private var creatorId: Long = 0
private var postId: Long = 0 private var postId: Long = 0
private var existOrdered: Boolean = false
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, inflater: LayoutInflater,
@@ -43,6 +44,7 @@ class CreatorCommunityCommentListFragment : BaseFragment<FragmentAudioContentCom
): View? { ): View? {
creatorId = arguments?.getLong(Constants.EXTRA_COMMUNITY_CREATOR_ID) ?: 0 creatorId = arguments?.getLong(Constants.EXTRA_COMMUNITY_CREATOR_ID) ?: 0
postId = arguments?.getLong(Constants.EXTRA_COMMUNITY_POST_ID) ?: 0 postId = arguments?.getLong(Constants.EXTRA_COMMUNITY_POST_ID) ?: 0
existOrdered = arguments?.getBoolean(Constants.EXTRA_COMMUNITY_EXIST_ORDERED) == true
return super.onCreateView(inflater, container, savedInstanceState) return super.onCreateView(inflater, container, savedInstanceState)
} }
@@ -73,8 +75,20 @@ class CreatorCommunityCommentListFragment : BaseFragment<FragmentAudioContentCom
binding.ivCommentSend.setOnClickListener { binding.ivCommentSend.setOnClickListener {
hideKeyboard() hideKeyboard()
val comment = binding.etComment.text.toString() val comment = binding.etComment.text.toString()
val isSecret = binding.tvSecret.isSelected
viewModel.registerComment(postId, comment, isSecret = isSecret)
binding.etComment.setText("") binding.etComment.setText("")
viewModel.registerComment(postId, comment) binding.tvSecret.isSelected = false
}
if (existOrdered) {
binding.tvSecret.visibility = View.VISIBLE
binding.tvSecret.setOnClickListener {
binding.tvSecret.isSelected = !binding.tvSecret.isSelected
}
} else {
binding.tvSecret.visibility = View.GONE
} }
adapter = CreatorCommunityCommentAdapter( adapter = CreatorCommunityCommentAdapter(
@@ -210,10 +224,15 @@ class CreatorCommunityCommentListFragment : BaseFragment<FragmentAudioContentCom
} }
companion object { companion object {
fun newInstance(creatorId: Long, postId: Long): CreatorCommunityCommentListFragment { fun newInstance(
creatorId: Long,
postId: Long,
existOrdered: Boolean
): CreatorCommunityCommentListFragment {
val args = Bundle() val args = Bundle()
args.putLong(Constants.EXTRA_COMMUNITY_CREATOR_ID, creatorId) args.putLong(Constants.EXTRA_COMMUNITY_CREATOR_ID, creatorId)
args.putLong(Constants.EXTRA_COMMUNITY_POST_ID, postId) args.putLong(Constants.EXTRA_COMMUNITY_POST_ID, postId)
args.putBoolean(Constants.EXTRA_COMMUNITY_EXIST_ORDERED, existOrdered)
val fragment = CreatorCommunityCommentListFragment() val fragment = CreatorCommunityCommentListFragment()
fragment.arguments = args fragment.arguments = args

View File

@@ -87,7 +87,12 @@ class CreatorCommunityCommentListViewModel(
} }
} }
fun registerComment(postId: Long, comment: String, commentId: Long? = null) { fun registerComment(
postId: Long,
comment: String,
commentId: Long? = null,
isSecret: Boolean = false
) {
if (!_isLoading.value!!) { if (!_isLoading.value!!) {
_isLoading.value = true _isLoading.value = true
} }
@@ -97,6 +102,7 @@ class CreatorCommunityCommentListViewModel(
postId = postId, postId = postId,
comment = comment, comment = comment,
parentId = commentId, parentId = commentId,
isSecret = isSecret,
token = "Bearer ${SharedPreferenceManager.token}" token = "Bearer ${SharedPreferenceManager.token}"
) )
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())

View File

@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@@ -141,10 +142,15 @@
android:padding="10.3dp" android:padding="10.3dp"
android:visibility="gone"> android:visibility="gone">
<LinearLayout <RelativeLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal"> android:orientation="horizontal"
tools:ignore="RelativeOverlap">
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
@@ -165,6 +171,22 @@
tools:text="1,204" /> tools:text="1,204" />
</LinearLayout> </LinearLayout>
<TextView
android:id="@+id/tv_secret"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:button="@null"
android:drawablePadding="8dp"
android:fontFamily="@font/gmarket_sans_medium"
android:gravity="center"
android:text="비밀댓글"
android:textColor="@color/color_selected_secret"
android:textSize="12.5sp"
android:visibility="gone"
app:drawableStartCompat="@drawable/ic_select_square" />
</RelativeLayout>
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@@ -238,7 +260,7 @@
android:background="@drawable/bg_round_corner_5_3_333333" android:background="@drawable/bg_round_corner_5_3_333333"
android:gravity="center" android:gravity="center"
android:orientation="vertical" android:orientation="vertical"
android:visibility="visible"> android:visibility="gone">
<ImageView <ImageView
android:layout_width="wrap_content" android:layout_width="wrap_content"