diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 75bbdd2..042874c 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -95,8 +95,8 @@
-
+
diff --git a/app/src/main/java/kr/co/vividnext/sodalive/common/Constants.kt b/app/src/main/java/kr/co/vividnext/sodalive/common/Constants.kt
index bdf701a..1eab262 100644
--- a/app/src/main/java/kr/co/vividnext/sodalive/common/Constants.kt
+++ b/app/src/main/java/kr/co/vividnext/sodalive/common/Constants.kt
@@ -57,4 +57,8 @@ object Constants {
const val LIVE_SERVICE_NOTIFICATION_ID: Int = 2
const val ACTION_AUDIO_CONTENT_RECEIVER = "soda_live_action_content_receiver"
const val ACTION_MAIN_AUDIO_CONTENT_RECEIVER = "soda_live_action_main_content_receiver"
+
+ const val EXTRA_COMMUNITY_POST_ID = "community_post_id"
+ const val EXTRA_COMMUNITY_CREATOR_ID = "community_creator_id"
+ const val EXTRA_COMMUNITY_POST_COMMENT = "community_post_comment_id"
}
diff --git a/app/src/main/java/kr/co/vividnext/sodalive/di/AppDI.kt b/app/src/main/java/kr/co/vividnext/sodalive/di/AppDI.kt
index 8839b2f..cd468ac 100644
--- a/app/src/main/java/kr/co/vividnext/sodalive/di/AppDI.kt
+++ b/app/src/main/java/kr/co/vividnext/sodalive/di/AppDI.kt
@@ -32,6 +32,8 @@ import kr.co.vividnext.sodalive.explorer.ExplorerViewModel
import kr.co.vividnext.sodalive.explorer.profile.UserProfileViewModel
import kr.co.vividnext.sodalive.explorer.profile.creator_community.CreatorCommunityApi
import kr.co.vividnext.sodalive.explorer.profile.creator_community.CreatorCommunityRepository
+import kr.co.vividnext.sodalive.explorer.profile.creator_community.all.CreatorCommunityAllViewModel
+import kr.co.vividnext.sodalive.explorer.profile.creator_community.all.comment.CreatorCommunityCommentListViewModel
import kr.co.vividnext.sodalive.explorer.profile.donation.UserProfileDonationAllViewModel
import kr.co.vividnext.sodalive.explorer.profile.fantalk.UserProfileFantalkAllViewModel
import kr.co.vividnext.sodalive.explorer.profile.follow.UserFollowerListViewModel
@@ -215,6 +217,8 @@ class AppDI(private val context: Context, isDebugMode: Boolean) {
viewModel { AudioContentNewAllViewModel(get()) }
viewModel { AudioContentRankingAllViewModel(get()) }
viewModel { RouletteSettingsViewModel(get()) }
+ viewModel { CreatorCommunityAllViewModel(get()) }
+ viewModel { CreatorCommunityCommentListViewModel(get()) }
}
private val repositoryModule = module {
diff --git a/app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/UserProfileActivity.kt b/app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/UserProfileActivity.kt
index 8771025..959b29e 100644
--- a/app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/UserProfileActivity.kt
+++ b/app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/UserProfileActivity.kt
@@ -36,6 +36,7 @@ import kr.co.vividnext.sodalive.databinding.ActivityUserProfileBinding
import kr.co.vividnext.sodalive.databinding.ItemCreatorCommunityBinding
import kr.co.vividnext.sodalive.explorer.profile.cheers.UserProfileCheersAdapter
import kr.co.vividnext.sodalive.explorer.profile.creator_community.GetCommunityPostListResponse
+import kr.co.vividnext.sodalive.explorer.profile.creator_community.all.CreatorCommunityAllActivity
import kr.co.vividnext.sodalive.explorer.profile.donation.UserProfileDonationAdapter
import kr.co.vividnext.sodalive.explorer.profile.donation.UserProfileDonationAllViewActivity
import kr.co.vividnext.sodalive.explorer.profile.fantalk.UserProfileFantalkAllViewActivity
@@ -474,7 +475,13 @@ class UserProfileActivity : BaseActivity(
private fun setupCreatorCommunityView() {
binding.layoutCreatorCommunityPost.ivWrite.setOnClickListener { }
- binding.layoutCreatorCommunityPost.llAll.setOnClickListener { }
+ binding.layoutCreatorCommunityPost.llAll.setOnClickListener {
+ startActivity(
+ Intent(applicationContext, CreatorCommunityAllActivity::class.java).apply {
+ putExtra(Constants.EXTRA_USER_ID, userId)
+ }
+ )
+ }
}
private fun bindData() {
@@ -734,7 +741,13 @@ class UserProfileActivity : BaseActivity(
layout.tvLikeCount.text = "${item.likeCount}"
layout.tvCommentCount.text = "${item.commentCount}"
- layout.root.setOnClickListener { }
+ layout.root.setOnClickListener {
+ startActivity(
+ Intent(applicationContext, CreatorCommunityAllActivity::class.java).apply {
+ putExtra(Constants.EXTRA_USER_ID, userId)
+ }
+ )
+ }
if (index > 0) {
val lp = layout.root.layoutParams as LinearLayout.LayoutParams
diff --git a/app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/creator_community/CreatorCommunityApi.kt b/app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/creator_community/CreatorCommunityApi.kt
index b4fd776..4c0043a 100644
--- a/app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/creator_community/CreatorCommunityApi.kt
+++ b/app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/creator_community/CreatorCommunityApi.kt
@@ -1,9 +1,16 @@
package kr.co.vividnext.sodalive.explorer.profile.creator_community
import io.reactivex.rxjava3.core.Single
+import kr.co.vividnext.sodalive.audio_content.comment.ModifyCommentRequest
import kr.co.vividnext.sodalive.common.ApiResponse
+import kr.co.vividnext.sodalive.explorer.profile.creator_community.all.PostCommunityPostLikeRequest
+import kr.co.vividnext.sodalive.explorer.profile.creator_community.all.comment.CreateCommunityPostCommentRequest
+import retrofit2.http.Body
import retrofit2.http.GET
import retrofit2.http.Header
+import retrofit2.http.POST
+import retrofit2.http.PUT
+import retrofit2.http.Path
import retrofit2.http.Query
interface CreatorCommunityApi {
@@ -21,4 +28,40 @@ interface CreatorCommunityApi {
@Query("timezone") timezone: String,
@Header("Authorization") authHeader: String
): Single>>
+
+ @POST("/creator-community/like")
+ fun communityPostLike(
+ @Body request: PostCommunityPostLikeRequest,
+ @Header("Authorization") authHeader: String
+ ): Single>
+
+ @GET("/creator-community/{id}/comment")
+ fun getCommunityPostCommentList(
+ @Path("id") postId: Long,
+ @Query("page") page: Int,
+ @Query("size") size: Int,
+ @Query("timezone") timezone: String,
+ @Header("Authorization") authHeader: String
+ ): Single>
+
+ @POST("/creator-community/comment")
+ fun createCommunityPostComment(
+ @Body request: CreateCommunityPostCommentRequest,
+ @Header("Authorization") authHeader: String
+ ): Single>
+
+ @PUT("/creator-community/comment")
+ fun modifyComment(
+ @Body request: ModifyCommentRequest,
+ @Header("Authorization") authHeader: String
+ ): Single>
+
+ @GET("/creator-community/comment/{id}")
+ fun getCommentReplyList(
+ @Path("id") commentId: Long,
+ @Query("page") page: Int,
+ @Query("size") size: Int,
+ @Query("timezone") timezone: String,
+ @Header("Authorization") authHeader: String
+ ): Single>
}
diff --git a/app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/creator_community/CreatorCommunityRepository.kt b/app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/creator_community/CreatorCommunityRepository.kt
index 6b9e3bd..27db775 100644
--- a/app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/creator_community/CreatorCommunityRepository.kt
+++ b/app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/creator_community/CreatorCommunityRepository.kt
@@ -1,5 +1,8 @@
package kr.co.vividnext.sodalive.explorer.profile.creator_community
+import kr.co.vividnext.sodalive.audio_content.comment.ModifyCommentRequest
+import kr.co.vividnext.sodalive.explorer.profile.creator_community.all.PostCommunityPostLikeRequest
+import kr.co.vividnext.sodalive.explorer.profile.creator_community.all.comment.CreateCommunityPostCommentRequest
import java.util.TimeZone
class CreatorCommunityRepository(private val api: CreatorCommunityApi) {
@@ -22,4 +25,49 @@ class CreatorCommunityRepository(private val api: CreatorCommunityApi) {
timezone = TimeZone.getDefault().id,
authHeader = token
)
+
+ fun communityPostLike(postId: Long, token: String) = api.communityPostLike(
+ request = PostCommunityPostLikeRequest(postId = postId),
+ authHeader = token
+ )
+
+ fun getCommunityPostCommentList(
+ postId: Long,
+ page: Int,
+ size: Int,
+ token: String
+ ) = api.getCommunityPostCommentList(
+ postId = postId,
+ page = page,
+ size = size,
+ timezone = TimeZone.getDefault().id,
+ authHeader = token
+ )
+
+ fun registerComment(
+ postId: Long,
+ comment: String,
+ parentId: Long? = null,
+ token: String
+ ) = api.createCommunityPostComment(
+ request = CreateCommunityPostCommentRequest(
+ comment = comment,
+ postId = postId,
+ parentId = parentId
+ ),
+ authHeader = token
+ )
+
+ fun modifyComment(request: ModifyCommentRequest, token: String) = api.modifyComment(
+ request = request,
+ authHeader = token
+ )
+
+ fun getCommentReplyList(commentId: Long, page: Int, size: Int, token: String) = api.getCommentReplyList(
+ commentId = commentId,
+ page = page,
+ size = size,
+ timezone = TimeZone.getDefault().id,
+ authHeader = token
+ )
}
diff --git a/app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/creator_community/GetCommunityPostCommentListResponse.kt b/app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/creator_community/GetCommunityPostCommentListResponse.kt
index 9e36e35..c8a3297 100644
--- a/app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/creator_community/GetCommunityPostCommentListResponse.kt
+++ b/app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/creator_community/GetCommunityPostCommentListResponse.kt
@@ -1,12 +1,15 @@
package kr.co.vividnext.sodalive.explorer.profile.creator_community
+import android.os.Parcelable
import com.google.gson.annotations.SerializedName
+import kotlinx.parcelize.Parcelize
data class GetCommunityPostCommentListResponse(
@SerializedName("totalCount") val totalCount: Int,
@SerializedName("items") val items: List
)
+@Parcelize
data class GetCommunityPostCommentListItem(
@SerializedName("id") val id: Long,
@SerializedName("writerId") val writerId: Long,
@@ -15,4 +18,4 @@ data class GetCommunityPostCommentListItem(
@SerializedName("comment") val comment: String,
@SerializedName("date") val date: String,
@SerializedName("replyCount") val replyCount: Int,
-)
+) : Parcelable
diff --git a/app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/creator_community/GetCommunityPostListResponse.kt b/app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/creator_community/GetCommunityPostListResponse.kt
index e8d05c0..501d78a 100644
--- a/app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/creator_community/GetCommunityPostListResponse.kt
+++ b/app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/creator_community/GetCommunityPostListResponse.kt
@@ -12,7 +12,7 @@ data class GetCommunityPostListResponse(
@SerializedName("date") val date: String,
@SerializedName("isCommentAvailable") val isCommentAvailable: Boolean,
@SerializedName("isAdult") val isAdult: Boolean,
- @SerializedName("isLike") val isLike: Boolean,
+ @SerializedName("isLike") var isLike: Boolean,
@SerializedName("likeCount") val likeCount: Int,
@SerializedName("commentCount") val commentCount: Int,
@SerializedName("firstComment") val firstComment: GetCommunityPostCommentListItem?
diff --git a/app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/creator_community/all/CreatorCommunityAllActivity.kt b/app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/creator_community/all/CreatorCommunityAllActivity.kt
new file mode 100644
index 0000000..d0bcb62
--- /dev/null
+++ b/app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/creator_community/all/CreatorCommunityAllActivity.kt
@@ -0,0 +1,132 @@
+package kr.co.vividnext.sodalive.explorer.profile.creator_community.all
+
+import android.annotation.SuppressLint
+import android.graphics.Rect
+import android.os.Bundle
+import android.view.View
+import android.widget.Toast
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+import kr.co.vividnext.sodalive.base.BaseActivity
+import kr.co.vividnext.sodalive.common.Constants
+import kr.co.vividnext.sodalive.common.LoadingDialog
+import kr.co.vividnext.sodalive.databinding.ActivityCreatorCommunityAllBinding
+import kr.co.vividnext.sodalive.explorer.profile.creator_community.all.comment.CreatorCommunityCommentFragment
+import kr.co.vividnext.sodalive.extensions.dpToPx
+import org.koin.android.ext.android.inject
+
+class CreatorCommunityAllActivity : BaseActivity(
+ ActivityCreatorCommunityAllBinding::inflate
+) {
+
+ private val viewModel: CreatorCommunityAllViewModel by inject()
+
+ private lateinit var loadingDialog: LoadingDialog
+ private lateinit var adapter: CreatorCommunityAllAdapter
+
+ private var creatorId: Long = 0
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ creatorId = intent.getLongExtra(Constants.EXTRA_USER_ID, 0)
+ if (creatorId <= 0) {
+ Toast.makeText(applicationContext, "잘못된 요청입니다.", Toast.LENGTH_LONG).show()
+ finish()
+ }
+
+ bindData()
+ viewModel.creatorId = creatorId
+ viewModel.getCommunityPostList()
+ }
+
+ override fun setupView() {
+ loadingDialog = LoadingDialog(this, layoutInflater)
+ binding.toolbar.tvBack.text = "커뮤니티"
+ binding.toolbar.tvBack.setOnClickListener { finish() }
+
+ adapter = CreatorCommunityAllAdapter(
+ onClickLike = { viewModel.communityPostLike(it) },
+ writeComment = { postId, parentId, comment ->
+ viewModel.registerComment(
+ comment,
+ postId,
+ parentId
+ )
+ },
+ showCommentBottomSheetDialog = {
+ val dialog = CreatorCommunityCommentFragment(
+ creatorId = creatorId,
+ postId = it
+ )
+ dialog.show(
+ supportFragmentManager,
+ dialog.tag
+ )
+ }
+ )
+
+ val recyclerView = binding.rvCreatorCommunity
+ recyclerView.layoutManager = LinearLayoutManager(
+ applicationContext,
+ LinearLayoutManager.VERTICAL,
+ false
+ )
+
+ recyclerView.addItemDecoration(object : RecyclerView.ItemDecoration() {
+ override fun getItemOffsets(
+ outRect: Rect,
+ view: View,
+ parent: RecyclerView,
+ state: RecyclerView.State
+ ) {
+ super.getItemOffsets(outRect, view, parent, state)
+
+ outRect.left = 6.7f.dpToPx().toInt()
+ outRect.right = 6.7f.dpToPx().toInt()
+
+ when (parent.getChildAdapterPosition(view)) {
+ 0 -> {
+ outRect.top = 13.3f.dpToPx().toInt()
+ outRect.bottom = 6.7f.dpToPx().toInt()
+ }
+
+ adapter.itemCount - 1 -> {
+ outRect.top = 6.7f.dpToPx().toInt()
+ outRect.bottom = 13.3f.dpToPx().toInt()
+ }
+
+ else -> {
+ outRect.top = 6.7f.dpToPx().toInt()
+ outRect.bottom = 6.7f.dpToPx().toInt()
+ }
+ }
+ }
+ })
+ recyclerView.adapter = adapter
+ }
+
+ @SuppressLint("NotifyDataSetChanged")
+ private fun bindData() {
+ viewModel.toastLiveData.observe(this) {
+ it?.let { Toast.makeText(applicationContext, it, Toast.LENGTH_LONG).show() }
+ }
+
+ viewModel.isLoading.observe(this) {
+ if (it) {
+ loadingDialog.show(screenWidth, "")
+ } else {
+ loadingDialog.dismiss()
+ }
+ }
+
+ viewModel.communityPostListLiveData.observe(this) {
+ if (viewModel.page == 2) {
+ adapter.items.clear()
+ }
+
+ adapter.items.addAll(it)
+ adapter.notifyDataSetChanged()
+ }
+ }
+}
diff --git a/app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/creator_community/all/CreatorCommunityAllAdapter.kt b/app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/creator_community/all/CreatorCommunityAllAdapter.kt
new file mode 100644
index 0000000..2e3d4c8
--- /dev/null
+++ b/app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/creator_community/all/CreatorCommunityAllAdapter.kt
@@ -0,0 +1,120 @@
+package kr.co.vividnext.sodalive.explorer.profile.creator_community.all
+
+import android.annotation.SuppressLint
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.recyclerview.widget.RecyclerView
+import coil.load
+import coil.transform.CircleCropTransformation
+import kr.co.vividnext.sodalive.R
+import kr.co.vividnext.sodalive.common.SharedPreferenceManager
+import kr.co.vividnext.sodalive.databinding.ItemCreatorCommunityAllBinding
+import kr.co.vividnext.sodalive.explorer.profile.creator_community.GetCommunityPostListResponse
+import kr.co.vividnext.sodalive.extensions.loadUrl
+
+class CreatorCommunityAllAdapter(
+ private val onClickLike: (Long) -> Unit,
+ private val writeComment: (Long, Long?, String) -> Unit,
+ private val showCommentBottomSheetDialog: (Long) -> Unit
+) : RecyclerView.Adapter() {
+
+ val items = mutableListOf()
+
+ inner class ViewHolder(
+ private val binding: ItemCreatorCommunityAllBinding
+ ) : RecyclerView.ViewHolder(binding.root) {
+ @SuppressLint("NotifyDataSetChanged")
+ fun bind(item: GetCommunityPostListResponse, index: Int) {
+ binding.tvNickname.text = item.creatorNickname
+ binding.ivCreatorProfile.loadUrl(item.creatorProfileUrl) {
+ crossfade(true)
+ placeholder(R.drawable.bg_placeholder)
+ transformations(CircleCropTransformation())
+ }
+
+ binding.tvContent.text = item.content
+
+ binding.ivLike.setImageResource(
+ if (item.isLike) {
+ R.drawable.ic_audio_content_heart_pressed
+ } else {
+ R.drawable.ic_audio_content_heart_normal
+ }
+ )
+
+ binding.tvLike.text = "${item.likeCount}"
+ binding.llLike.setOnClickListener {
+ val isLike = !item.isLike
+
+ items[index] = items[index].copy(
+ isLike = !item.isLike,
+ likeCount = if (isLike) item.likeCount + 1 else item.likeCount - 1
+ )
+ notifyDataSetChanged()
+
+ onClickLike(item.postId)
+ }
+
+ if (item.imageUrl != null) {
+ binding.ivContent.visibility = View.VISIBLE
+ binding.ivContent.loadUrl(item.imageUrl) {
+ crossfade(true)
+ placeholder(R.drawable.bg_placeholder)
+ }
+ } else {
+ binding.ivContent.visibility = View.GONE
+ }
+
+ if (item.isCommentAvailable) {
+ binding.llComment.visibility = View.VISIBLE
+ binding.tvCommentCount.text = "${item.commentCount}"
+ } else {
+ binding.llComment.visibility = View.GONE
+ }
+
+ if (item.commentCount > 0) {
+ binding.ivCommentProfile.load(item.firstComment!!.profileUrl) {
+ crossfade(true)
+ placeholder(R.drawable.bg_placeholder)
+ transformations(CircleCropTransformation())
+ }
+ binding.tvCommentText.text = item.firstComment.comment
+ binding.tvCommentText.visibility = View.VISIBLE
+ binding.rlInputComment.visibility = View.GONE
+
+ binding.llComment.setOnClickListener { showCommentBottomSheetDialog(item.postId) }
+ } else {
+ binding.tvCommentText.visibility = View.GONE
+ binding.rlInputComment.visibility = View.VISIBLE
+ binding.ivCommentProfile.load(SharedPreferenceManager.profileImage) {
+ crossfade(true)
+ placeholder(R.drawable.bg_placeholder)
+ transformations(CircleCropTransformation())
+ }
+
+ binding.ivCommentSend.setOnClickListener {
+ val comment = binding.etComment.text.toString()
+ binding.etComment.setText("")
+ writeComment(item.postId, null, comment)
+ }
+
+ binding.llComment.setOnClickListener {}
+ }
+ }
+ }
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder(
+ ItemCreatorCommunityAllBinding.inflate(
+ LayoutInflater.from(parent.context),
+ parent,
+ false
+ )
+ )
+
+ override fun getItemCount() = items.size
+
+ override fun onBindViewHolder(holder: ViewHolder, position: Int) {
+ holder.bind(items[position], position)
+ }
+}
diff --git a/app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/creator_community/all/CreatorCommunityAllViewModel.kt b/app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/creator_community/all/CreatorCommunityAllViewModel.kt
new file mode 100644
index 0000000..9732da6
--- /dev/null
+++ b/app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/creator_community/all/CreatorCommunityAllViewModel.kt
@@ -0,0 +1,131 @@
+package kr.co.vividnext.sodalive.explorer.profile.creator_community.all
+
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import com.orhanobut.logger.Logger
+import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
+import io.reactivex.rxjava3.schedulers.Schedulers
+import kr.co.vividnext.sodalive.base.BaseViewModel
+import kr.co.vividnext.sodalive.common.SharedPreferenceManager
+import kr.co.vividnext.sodalive.explorer.profile.creator_community.CreatorCommunityRepository
+import kr.co.vividnext.sodalive.explorer.profile.creator_community.GetCommunityPostListResponse
+
+class CreatorCommunityAllViewModel(
+ private val repository: CreatorCommunityRepository
+) : BaseViewModel() {
+ private val _toastLiveData = MutableLiveData()
+ val toastLiveData: LiveData
+ get() = _toastLiveData
+
+ private var _isLoading = MutableLiveData(false)
+ val isLoading: LiveData
+ get() = _isLoading
+
+ private var _communityPostListLiveData = MutableLiveData>()
+ val communityPostListLiveData: LiveData>
+ get() = _communityPostListLiveData
+
+ var page = 1
+ var isLast = false
+ private val pageSize = 10
+
+ var creatorId = 0L
+
+ fun getCommunityPostList() {
+ if (!_isLoading.value!! && !isLast) {
+ _isLoading.value = true
+ compositeDisposable.add(
+ repository
+ .getCommunityPostList(
+ creatorId = creatorId,
+ page = page,
+ size = pageSize,
+ token = "Bearer ${SharedPreferenceManager.token}"
+ )
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(
+ {
+ if (it.success && it.data != null) {
+ if (it.data.isNotEmpty()) {
+ page += 1
+ _communityPostListLiveData.postValue(it.data!!)
+ } else {
+ isLast = true
+ }
+ } else {
+ if (it.message != null) {
+ _toastLiveData.postValue(it.message)
+ } else {
+ _toastLiveData.postValue(
+ "알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
+ )
+ }
+ }
+
+ _isLoading.value = false
+ },
+ {
+ _isLoading.value = false
+ it.message?.let { message -> Logger.e(message) }
+ _toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
+ }
+ )
+ )
+ }
+ }
+
+ fun communityPostLike(postId: Long) {
+ compositeDisposable.add(
+ repository.communityPostLike(
+ postId = postId,
+ token = "Bearer ${SharedPreferenceManager.token}"
+ )
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe({}, {})
+ )
+
+ }
+
+ fun registerComment(comment: String, postId: Long, parentId: Long? = null) {
+ if (!_isLoading.value!!) {
+ _isLoading.value = true
+ }
+
+ compositeDisposable.add(
+ repository.registerComment(
+ postId = postId,
+ comment = comment,
+ parentId = parentId,
+ token = "Bearer ${SharedPreferenceManager.token}"
+ )
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(
+ {
+ _isLoading.value = false
+
+ if (it.success) {
+ page = 1
+ isLast = false
+ getCommunityPostList()
+ } else {
+ if (it.message != null) {
+ _toastLiveData.postValue(it.message)
+ } else {
+ _toastLiveData.postValue(
+ "알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
+ )
+ }
+ }
+ },
+ {
+ _isLoading.value = false
+ it.message?.let { message -> Logger.e(message) }
+ _toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
+ }
+ )
+ )
+ }
+}
diff --git a/app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/creator_community/all/PostCommunityPostLikeRequest.kt b/app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/creator_community/all/PostCommunityPostLikeRequest.kt
new file mode 100644
index 0000000..4d03c63
--- /dev/null
+++ b/app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/creator_community/all/PostCommunityPostLikeRequest.kt
@@ -0,0 +1,7 @@
+package kr.co.vividnext.sodalive.explorer.profile.creator_community.all
+
+import com.google.gson.annotations.SerializedName
+
+data class PostCommunityPostLikeRequest(
+ @SerializedName("postId") val postId: Long
+)
diff --git a/app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/creator_community/all/comment/CreateCommunityPostCommentRequest.kt b/app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/creator_community/all/comment/CreateCommunityPostCommentRequest.kt
new file mode 100644
index 0000000..f1acf95
--- /dev/null
+++ b/app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/creator_community/all/comment/CreateCommunityPostCommentRequest.kt
@@ -0,0 +1,9 @@
+package kr.co.vividnext.sodalive.explorer.profile.creator_community.all.comment
+
+import com.google.gson.annotations.SerializedName
+
+data class CreateCommunityPostCommentRequest(
+ @SerializedName("comment") val comment: String,
+ @SerializedName("postId") val postId: Long,
+ @SerializedName("parentId") val parentId: Long?
+)
diff --git a/app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/creator_community/all/comment/CreatorCommunityCommentAdapter.kt b/app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/creator_community/all/comment/CreatorCommunityCommentAdapter.kt
new file mode 100644
index 0000000..3b0af50
--- /dev/null
+++ b/app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/creator_community/all/comment/CreatorCommunityCommentAdapter.kt
@@ -0,0 +1,128 @@
+package kr.co.vividnext.sodalive.explorer.profile.creator_community.all.comment
+
+import android.content.Context
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.appcompat.widget.PopupMenu
+import androidx.recyclerview.widget.RecyclerView
+import coil.load
+import coil.transform.CircleCropTransformation
+import kr.co.vividnext.sodalive.R
+import kr.co.vividnext.sodalive.common.SharedPreferenceManager
+import kr.co.vividnext.sodalive.databinding.ItemCommunityPostCommentBinding
+import kr.co.vividnext.sodalive.explorer.profile.creator_community.GetCommunityPostCommentListItem
+
+class CreatorCommunityCommentAdapter(
+ private val creatorId: Long,
+ private val modifyComment: (Long, String) -> Unit,
+ private val onClickDelete: (Long) -> Unit,
+ private val onItemClick: (GetCommunityPostCommentListItem) -> Unit
+) : RecyclerView.Adapter() {
+ var items = mutableSetOf()
+
+ inner class ViewHolder(
+ private val context: Context,
+ private val binding: ItemCommunityPostCommentBinding
+ ) : RecyclerView.ViewHolder(binding.root) {
+
+ fun bind(item: GetCommunityPostCommentListItem) {
+ binding.ivCommentProfile.load(item.profileUrl) {
+ crossfade(true)
+ placeholder(R.drawable.bg_placeholder)
+ transformations(CircleCropTransformation())
+ }
+
+ binding.tvComment.text = item.comment
+ binding.tvCommentDate.text = item.date
+ binding.tvCommentNickname.text = item.nickname
+
+ binding.tvWriteReply.text = if (item.replyCount > 0) {
+ "답글 ${item.replyCount}개"
+ } else {
+ "답글 쓰기"
+ }
+
+ if (
+ item.writerId == SharedPreferenceManager.userId ||
+ creatorId == SharedPreferenceManager.userId
+ ) {
+ binding.etCommentModify.setText(item.comment)
+ binding.ivMenu.visibility = View.VISIBLE
+ binding.ivMenu.setOnClickListener {
+ showOptionMenu(
+ context,
+ binding.ivMenu,
+ commentId = item.id,
+ writerId = item.writerId,
+ creatorId = creatorId,
+ onClickModify = {
+ binding.rlCommentModify.visibility = View.VISIBLE
+ binding.tvComment.visibility = View.GONE
+ }
+ )
+ }
+
+ binding.tvModify.setOnClickListener {
+ binding.rlCommentModify.visibility = View.GONE
+ binding.tvComment.visibility = View.VISIBLE
+ modifyComment(item.id, binding.etCommentModify.text.toString())
+ }
+ } else {
+ binding.ivMenu.visibility = View.GONE
+ }
+
+ binding.tvWriteReply.setOnClickListener { onItemClick(item) }
+ binding.root.setOnClickListener { onItemClick(item) }
+ }
+ }
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder(
+ parent.context,
+ ItemCommunityPostCommentBinding.inflate(
+ LayoutInflater.from(parent.context),
+ parent,
+ false
+ )
+ )
+
+ override fun onBindViewHolder(holder: ViewHolder, position: Int) {
+ holder.bind(items.toList()[position])
+ }
+
+ override fun getItemCount() = items.size
+
+ private fun showOptionMenu(
+ context: Context,
+ v: View,
+ commentId: Long,
+ writerId: Long,
+ creatorId: Long,
+ onClickModify: () -> Unit
+ ) {
+ val popup = PopupMenu(context, v)
+ val inflater = popup.menuInflater
+
+ if (writerId == SharedPreferenceManager.userId) {
+ inflater.inflate(R.menu.content_comment_option_menu, popup.menu)
+ } else if (creatorId == SharedPreferenceManager.userId) {
+ inflater.inflate(R.menu.content_comment_option_menu2, popup.menu)
+ }
+
+ popup.setOnMenuItemClickListener {
+ when (it.itemId) {
+ R.id.menu_review_modify -> {
+ onClickModify()
+ }
+
+ R.id.menu_review_delete -> {
+ onClickDelete(commentId)
+ }
+ }
+
+ true
+ }
+
+ popup.show()
+ }
+}
diff --git a/app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/creator_community/all/comment/CreatorCommunityCommentFragment.kt b/app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/creator_community/all/comment/CreatorCommunityCommentFragment.kt
new file mode 100644
index 0000000..471cf40
--- /dev/null
+++ b/app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/creator_community/all/comment/CreatorCommunityCommentFragment.kt
@@ -0,0 +1,78 @@
+package kr.co.vividnext.sodalive.explorer.profile.creator_community.all.comment
+
+import android.app.Dialog
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.FrameLayout
+import com.google.android.material.bottomsheet.BottomSheetBehavior
+import com.google.android.material.bottomsheet.BottomSheetDialog
+import com.google.android.material.bottomsheet.BottomSheetDialogFragment
+import kr.co.vividnext.sodalive.R
+import kr.co.vividnext.sodalive.databinding.DialogAudioContentCommentBinding
+import kr.co.vividnext.sodalive.explorer.profile.creator_community.GetCommunityPostCommentListItem
+
+class CreatorCommunityCommentFragment(
+ private val creatorId: Long,
+ private val postId: Long
+) : BottomSheetDialogFragment() {
+
+ private lateinit var binding: DialogAudioContentCommentBinding
+
+ override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
+ val dialog = super.onCreateDialog(savedInstanceState)
+
+ dialog.setOnShowListener {
+ val d = it as BottomSheetDialog
+ val bottomSheet = d.findViewById(
+ com.google.android.material.R.id.design_bottom_sheet
+ )
+ if (bottomSheet != null) {
+ BottomSheetBehavior.from(bottomSheet).state = BottomSheetBehavior.STATE_EXPANDED
+ }
+ }
+
+ return dialog
+ }
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View {
+ binding = DialogAudioContentCommentBinding.inflate(inflater, container, false)
+ return binding.root
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+
+ val commentListFragmentTag = "COMMENT_LIST_FRAGMENT"
+ val commentListFragment = CreatorCommunityCommentListFragment.newInstance(
+ creatorId = creatorId,
+ postId = postId
+ )
+ val fragmentTransaction = childFragmentManager.beginTransaction()
+ fragmentTransaction.add(R.id.fl_container, commentListFragment, commentListFragmentTag)
+ fragmentTransaction.addToBackStack(commentListFragmentTag)
+ fragmentTransaction.commit()
+ }
+
+ fun hideCommentDialog() {
+ dialog?.dismiss()
+ }
+
+ fun onClickComment(comment: GetCommunityPostCommentListItem) {
+ val commentReplyFragmentTag = "COMMENT_REPLY_FRAGMENT"
+ val commentReplyFragment = CreatorCommunityCommentReplyFragment.newInstance(
+ creatorId = creatorId,
+ postId = postId,
+ comment = comment
+ )
+ val fragmentTransaction = childFragmentManager.beginTransaction()
+ fragmentTransaction.add(R.id.fl_container, commentReplyFragment, commentReplyFragmentTag)
+ fragmentTransaction.addToBackStack(commentReplyFragmentTag)
+ fragmentTransaction.commit()
+ }
+}
diff --git a/app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/creator_community/all/comment/CreatorCommunityCommentListFragment.kt b/app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/creator_community/all/comment/CreatorCommunityCommentListFragment.kt
new file mode 100644
index 0000000..3e09c41
--- /dev/null
+++ b/app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/creator_community/all/comment/CreatorCommunityCommentListFragment.kt
@@ -0,0 +1,215 @@
+package kr.co.vividnext.sodalive.explorer.profile.creator_community.all.comment
+
+import android.annotation.SuppressLint
+import android.app.Service
+import android.graphics.Rect
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.view.inputmethod.InputMethodManager
+import android.widget.Toast
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+import coil.load
+import coil.transform.CircleCropTransformation
+import kr.co.vividnext.sodalive.R
+import kr.co.vividnext.sodalive.audio_content.comment.AudioContentCommentFragment
+import kr.co.vividnext.sodalive.base.BaseFragment
+import kr.co.vividnext.sodalive.base.SodaDialog
+import kr.co.vividnext.sodalive.common.Constants
+import kr.co.vividnext.sodalive.common.LoadingDialog
+import kr.co.vividnext.sodalive.common.SharedPreferenceManager
+import kr.co.vividnext.sodalive.databinding.FragmentAudioContentCommentListBinding
+import kr.co.vividnext.sodalive.extensions.dpToPx
+import org.koin.android.ext.android.inject
+
+class CreatorCommunityCommentListFragment : BaseFragment(
+ FragmentAudioContentCommentListBinding::inflate
+) {
+ private val viewModel: CreatorCommunityCommentListViewModel by inject()
+
+ private lateinit var imm: InputMethodManager
+ private lateinit var loadingDialog: LoadingDialog
+ private lateinit var adapter: CreatorCommunityCommentAdapter
+
+ private var creatorId: Long = 0
+ private var postId: Long = 0
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View? {
+ creatorId = arguments?.getLong(Constants.EXTRA_COMMUNITY_CREATOR_ID) ?: 0
+ postId = arguments?.getLong(Constants.EXTRA_COMMUNITY_POST_ID) ?: 0
+ return super.onCreateView(inflater, container, savedInstanceState)
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ loadingDialog = LoadingDialog(requireActivity(), layoutInflater)
+ imm = requireContext().getSystemService(
+ Service.INPUT_METHOD_SERVICE
+ ) as InputMethodManager
+
+ setupView()
+ bindData()
+ viewModel.getCommentList(postId) { hideDialog() }
+ }
+
+ private fun hideDialog() {
+ (parentFragment as CreatorCommunityCommentFragment).hideCommentDialog()
+ }
+
+ private fun setupView() {
+ binding.ivClose.setOnClickListener { hideDialog() }
+
+ binding.ivCommentProfile.load(SharedPreferenceManager.profileImage) {
+ crossfade(true)
+ placeholder(R.drawable.ic_place_holder)
+ transformations(CircleCropTransformation())
+ }
+
+ binding.ivCommentSend.setOnClickListener {
+ hideKeyboard()
+ val comment = binding.etComment.text.toString()
+ binding.etComment.setText("")
+ viewModel.registerComment(postId, comment)
+ }
+
+ adapter = CreatorCommunityCommentAdapter(
+ creatorId = creatorId,
+ modifyComment = { commentId, comment ->
+ hideKeyboard()
+ viewModel.modifyComment(
+ commentId = commentId,
+ postId = postId,
+ comment = comment
+ )
+ },
+ onClickDelete = {
+ SodaDialog(
+ activity = requireActivity(),
+ layoutInflater = layoutInflater,
+ title = "댓글 삭제",
+ desc = "삭제하시겠습니까?",
+ confirmButtonTitle = "삭제",
+ confirmButtonClick = {
+ viewModel.modifyComment(
+ commentId = it,
+ postId = postId,
+ isActive = false
+ )
+ },
+ cancelButtonTitle = "취소",
+ cancelButtonClick = {}
+ ).show(screenWidth)
+ },
+ onItemClick = {
+ (parentFragment as CreatorCommunityCommentFragment).onClickComment(it)
+ }
+ )
+
+ val recyclerView = binding.rvComment
+ recyclerView.setHasFixedSize(true)
+ recyclerView.layoutManager = LinearLayoutManager(
+ activity,
+ LinearLayoutManager.VERTICAL,
+ false
+ )
+ recyclerView.addItemDecoration(object : RecyclerView.ItemDecoration() {
+ override fun getItemOffsets(
+ outRect: Rect,
+ view: View,
+ parent: RecyclerView,
+ state: RecyclerView.State
+ ) {
+ super.getItemOffsets(outRect, view, parent, state)
+
+ outRect.left = 13.3f.dpToPx().toInt()
+ outRect.right = 13.3f.dpToPx().toInt()
+
+ when (parent.getChildAdapterPosition(view)) {
+ 0 -> {
+ outRect.top = 13.3f.dpToPx().toInt()
+ outRect.bottom = 6.7f.dpToPx().toInt()
+ }
+
+ adapter.itemCount - 1 -> {
+ outRect.top = 6.7f.dpToPx().toInt()
+ outRect.bottom = 13.3f.dpToPx().toInt()
+ }
+
+ else -> {
+ outRect.top = 6.7f.dpToPx().toInt()
+ outRect.bottom = 6.7f.dpToPx().toInt()
+ }
+ }
+ }
+ })
+
+ recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
+ override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
+ super.onScrolled(recyclerView, dx, dy)
+
+ val lastVisibleItemPosition = (recyclerView.layoutManager as LinearLayoutManager?)!!
+ .findLastCompletelyVisibleItemPosition()
+ val itemTotalCount = recyclerView.adapter!!.itemCount - 1
+
+ // 스크롤이 끝에 도달했는지 확인
+ if (!recyclerView.canScrollVertically(1) &&
+ lastVisibleItemPosition == itemTotalCount
+ ) {
+ viewModel.getCommentList(postId = postId)
+ }
+ }
+ })
+
+ recyclerView.adapter = adapter
+ }
+
+ @SuppressLint("NotifyDataSetChanged")
+ private fun bindData() {
+ viewModel.isLoading.observe(viewLifecycleOwner) {
+ if (it) {
+ loadingDialog.show(screenWidth)
+ } else {
+ loadingDialog.dismiss()
+ }
+ }
+
+ viewModel.toastLiveData.observe(viewLifecycleOwner) {
+ it?.let { Toast.makeText(requireContext(), it, Toast.LENGTH_LONG).show() }
+ }
+
+ viewModel.totalCommentCount.observe(viewLifecycleOwner) {
+ binding.tvCommentCount.text = "$it"
+ }
+
+ viewModel.commentList.observe(viewLifecycleOwner) {
+ if (viewModel.page - 1 == 1) {
+ adapter.items.clear()
+ binding.rvComment.scrollToPosition(0)
+ }
+
+ adapter.items.addAll(it)
+ adapter.notifyDataSetChanged()
+ }
+ }
+
+ private fun hideKeyboard() {
+ imm.hideSoftInputFromWindow(view?.windowToken, 0)
+ }
+
+ companion object {
+ fun newInstance(creatorId: Long, postId: Long): CreatorCommunityCommentListFragment {
+ val args = Bundle()
+ args.putLong(Constants.EXTRA_COMMUNITY_CREATOR_ID, creatorId)
+ args.putLong(Constants.EXTRA_COMMUNITY_POST_ID, postId)
+
+ val fragment = CreatorCommunityCommentListFragment()
+ fragment.arguments = args
+ return fragment
+ }
+ }
+}
diff --git a/app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/creator_community/all/comment/CreatorCommunityCommentListViewModel.kt b/app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/creator_community/all/comment/CreatorCommunityCommentListViewModel.kt
new file mode 100644
index 0000000..f1cb2b6
--- /dev/null
+++ b/app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/creator_community/all/comment/CreatorCommunityCommentListViewModel.kt
@@ -0,0 +1,248 @@
+package kr.co.vividnext.sodalive.explorer.profile.creator_community.all.comment
+
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import com.orhanobut.logger.Logger
+import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
+import io.reactivex.rxjava3.schedulers.Schedulers
+import kr.co.vividnext.sodalive.audio_content.comment.ModifyCommentRequest
+import kr.co.vividnext.sodalive.base.BaseViewModel
+import kr.co.vividnext.sodalive.common.SharedPreferenceManager
+import kr.co.vividnext.sodalive.explorer.profile.creator_community.CreatorCommunityRepository
+import kr.co.vividnext.sodalive.explorer.profile.creator_community.GetCommunityPostCommentListItem
+
+class CreatorCommunityCommentListViewModel(
+ private val repository: CreatorCommunityRepository
+) : BaseViewModel() {
+ private val _toastLiveData = MutableLiveData()
+ val toastLiveData: LiveData
+ get() = _toastLiveData
+
+ private var _isLoading = MutableLiveData(false)
+ val isLoading: LiveData
+ get() = _isLoading
+
+ private var _commentList = MutableLiveData>()
+ val commentList: LiveData>
+ get() = _commentList
+
+ private var _totalCommentCount = MutableLiveData(0)
+ val totalCommentCount: LiveData
+ get() = _totalCommentCount
+
+ var page = 1
+ private var isLast = false
+ private val size = 10
+
+ fun getCommentList(postId: Long, onFailure: (() -> Unit)? = null) {
+ if (!_isLoading.value!! && !isLast) {
+ _isLoading.value = true
+ compositeDisposable.add(
+ repository.getCommunityPostCommentList(
+ postId = postId,
+ page = page - 1,
+ size = size,
+ token = "Bearer ${SharedPreferenceManager.token}"
+ )
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(
+ {
+ if (it.success && it.data != null) {
+ _totalCommentCount.postValue(it.data.totalCount)
+
+ page += 1
+ if (it.data.items.isNotEmpty()) {
+ _commentList.postValue(it.data.items)
+ } else {
+ isLast = true
+ _commentList.postValue(listOf())
+ }
+ } else {
+ if (it.message != null) {
+ _toastLiveData.postValue(it.message)
+ } else {
+ _toastLiveData.postValue(
+ "알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
+ )
+ }
+
+ if (onFailure != null) {
+ onFailure()
+ }
+ }
+
+ _isLoading.value = false
+ },
+ {
+ _isLoading.value = false
+ it.message?.let { message -> Logger.e(message) }
+ _toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
+ if (onFailure != null) {
+ onFailure()
+ }
+ }
+ )
+ )
+ }
+ }
+
+ fun registerComment(postId: Long, comment: String, commentId: Long? = null) {
+ if (!_isLoading.value!!) {
+ _isLoading.value = true
+ }
+
+ compositeDisposable.add(
+ repository.registerComment(
+ postId = postId,
+ comment = comment,
+ parentId = commentId,
+ token = "Bearer ${SharedPreferenceManager.token}"
+ )
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(
+ {
+ _isLoading.value = false
+
+ if (it.success) {
+ page = 1
+ isLast = false
+ if (commentId != null) {
+ getCommentReplyList(commentId = commentId)
+ } else {
+ getCommentList(postId)
+ }
+ } else {
+ if (it.message != null) {
+ _toastLiveData.postValue(it.message)
+ } else {
+ _toastLiveData.postValue(
+ "알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
+ )
+ }
+ }
+ },
+ {
+ _isLoading.value = false
+ it.message?.let { message -> Logger.e(message) }
+ _toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
+ }
+ )
+ )
+ }
+
+ fun modifyComment(
+ commentId: Long,
+ postId: Long,
+ parentCommentId: Long? = null,
+ comment: String? = null,
+ isActive: Boolean? = null
+ ) {
+ if (comment == null && isActive == null) {
+ _toastLiveData.postValue("변경사항이 없습니다.")
+ return
+ }
+
+ if (comment != null && comment.isBlank()) {
+ _toastLiveData.postValue("내용을 입력하세요")
+ return
+ }
+
+ _isLoading.value = true
+
+ val request = ModifyCommentRequest(commentId = commentId)
+
+ if (comment != null) {
+ request.comment = comment
+ }
+
+ if (isActive != null) {
+ request.isActive = isActive
+ }
+
+ compositeDisposable.add(
+ repository.modifyComment(
+ request = request,
+ token = "Bearer ${SharedPreferenceManager.token}"
+ )
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(
+ {
+ _isLoading.value = false
+
+ if (it.success) {
+ page = 1
+ isLast = false
+
+ if (parentCommentId != null) {
+ getCommentReplyList(parentCommentId)
+ } else {
+ getCommentList(postId)
+ }
+ } else {
+ val message = it.message ?: "알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
+ _toastLiveData.postValue(message)
+ }
+ },
+ {
+ _isLoading.value = false
+ it.message?.let { message -> Logger.e(message) }
+ _toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
+ }
+ )
+ )
+ }
+
+ fun getCommentReplyList(commentId: Long, onFailure: (() -> Unit)? = null) {
+ if (!_isLoading.value!! && !isLast) {
+ _isLoading.value = true
+ compositeDisposable.add(
+ repository.getCommentReplyList(
+ commentId = commentId,
+ page = page - 1,
+ size = size,
+ token = "Bearer ${SharedPreferenceManager.token}"
+ )
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(
+ {
+ if (it.success && it.data != null) {
+ page += 1
+ if (it.data.items.isNotEmpty()) {
+ _commentList.postValue(it.data.items)
+ } else {
+ isLast = true
+ _commentList.postValue(listOf())
+ }
+ } else {
+ if (it.message != null) {
+ _toastLiveData.postValue(it.message)
+ } else {
+ _toastLiveData.postValue(
+ "알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
+ )
+ }
+
+ if (onFailure != null) {
+ onFailure()
+ }
+ }
+
+ _isLoading.value = false
+ },
+ {
+ _isLoading.value = false
+ it.message?.let { message -> Logger.e(message) }
+ _toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
+ if (onFailure != null) {
+ onFailure()
+ }
+ }
+ )
+ )
+ }
+ }
+}
diff --git a/app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/creator_community/all/comment/CreatorCommunityCommentReplyAdapter.kt b/app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/creator_community/all/comment/CreatorCommunityCommentReplyAdapter.kt
new file mode 100644
index 0000000..f1fd79b
--- /dev/null
+++ b/app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/creator_community/all/comment/CreatorCommunityCommentReplyAdapter.kt
@@ -0,0 +1,173 @@
+package kr.co.vividnext.sodalive.explorer.profile.creator_community.all.comment
+
+import android.content.Context
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.appcompat.widget.PopupMenu
+import androidx.recyclerview.widget.RecyclerView
+import androidx.viewbinding.ViewBinding
+import coil.load
+import coil.transform.CircleCropTransformation
+import kr.co.vividnext.sodalive.R
+import kr.co.vividnext.sodalive.common.SharedPreferenceManager
+import kr.co.vividnext.sodalive.databinding.ItemCommunityPostCommentBinding
+import kr.co.vividnext.sodalive.databinding.ItemCommunityPostCommentReplyBinding
+import kr.co.vividnext.sodalive.explorer.profile.creator_community.GetCommunityPostCommentListItem
+import kr.co.vividnext.sodalive.extensions.loadUrl
+
+class CreatorCommunityCommentReplyAdapter(
+ private val creatorId: Long,
+ private val modifyComment: (Long, String) -> Unit,
+ private val onClickDelete: (Long) -> Unit
+) : RecyclerView.Adapter() {
+ var items = mutableSetOf()
+ override fun onCreateViewHolder(
+ parent: ViewGroup,
+ viewType: Int
+ ): CreatorCommunityCommentReplyViewHolder {
+ return if (viewType == 0) {
+ CreatorCommunityCommentReplyHeaderViewHolder(
+ binding = ItemCommunityPostCommentBinding.inflate(
+ LayoutInflater.from(parent.context),
+ parent,
+ false
+ )
+ )
+ } else {
+ CreatorCommunityCommentReplyItemViewHolder(
+ context = parent.context,
+ creatorId = creatorId,
+ binding = ItemCommunityPostCommentReplyBinding.inflate(
+ LayoutInflater.from(parent.context),
+ parent,
+ false
+ ),
+ showOptionMenu = { context, view, commentId, writerId, creatorId, onClickModify ->
+ showOptionMenu(context, view, commentId, writerId, creatorId, onClickModify)
+ },
+ modifyComment = modifyComment
+ )
+ }
+ }
+
+ override fun onBindViewHolder(holder: CreatorCommunityCommentReplyViewHolder, position: Int) {
+ holder.bind(items.toList()[position])
+ }
+
+ override fun getItemCount() = items.size
+
+ override fun getItemViewType(position: Int): Int {
+ return position
+ }
+
+ private fun showOptionMenu(
+ context: Context,
+ v: View,
+ commentId: Long,
+ writerId: Long,
+ creatorId: Long,
+ onClickModify: () -> Unit
+ ) {
+ val popup = PopupMenu(context, v)
+ val inflater = popup.menuInflater
+
+ if (writerId == SharedPreferenceManager.userId) {
+ inflater.inflate(R.menu.content_comment_option_menu, popup.menu)
+ } else if (creatorId == SharedPreferenceManager.userId) {
+ inflater.inflate(R.menu.content_comment_option_menu2, popup.menu)
+ }
+
+ popup.setOnMenuItemClickListener {
+ when (it.itemId) {
+ R.id.menu_review_modify -> {
+ onClickModify()
+ }
+
+ R.id.menu_review_delete -> {
+ onClickDelete(commentId)
+ }
+ }
+
+ true
+ }
+
+ popup.show()
+ }
+}
+
+abstract class CreatorCommunityCommentReplyViewHolder(
+ binding: ViewBinding
+) : RecyclerView.ViewHolder(binding.root) {
+ abstract fun bind(item: GetCommunityPostCommentListItem)
+}
+
+class CreatorCommunityCommentReplyHeaderViewHolder(
+ private val binding: ItemCommunityPostCommentBinding
+) : CreatorCommunityCommentReplyViewHolder(binding) {
+ override fun bind(item: GetCommunityPostCommentListItem) {
+ binding.ivCommentProfile.loadUrl(item.profileUrl) {
+ crossfade(true)
+ placeholder(R.drawable.ic_place_holder)
+ transformations(CircleCropTransformation())
+ }
+
+ binding.tvComment.text = item.comment
+ binding.tvCommentDate.text = item.date
+ binding.tvCommentNickname.text = item.nickname
+
+ binding.tvWriteReply.visibility = View.GONE
+ binding.ivMenu.visibility = View.GONE
+ }
+}
+
+class CreatorCommunityCommentReplyItemViewHolder(
+ private val context: Context,
+ private val creatorId: Long,
+ private val binding: ItemCommunityPostCommentReplyBinding,
+ private val showOptionMenu: (
+ Context, View, Long, Long, Long, onClickModify: () -> Unit
+ ) -> Unit,
+ private val modifyComment: (Long, String) -> Unit
+) : CreatorCommunityCommentReplyViewHolder(binding) {
+ override fun bind(item: GetCommunityPostCommentListItem) {
+ binding.ivCommentProfile.load(item.profileUrl) {
+ crossfade(true)
+ placeholder(R.drawable.ic_place_holder)
+ transformations(CircleCropTransformation())
+ }
+
+ binding.tvComment.text = item.comment
+ binding.tvCommentDate.text = item.date
+ binding.tvCommentNickname.text = item.nickname
+
+ if (
+ item.writerId == SharedPreferenceManager.userId ||
+ creatorId == SharedPreferenceManager.userId
+ ) {
+ binding.etCommentModify.setText(item.comment)
+ binding.ivMenu.visibility = View.VISIBLE
+ binding.ivMenu.setOnClickListener {
+ showOptionMenu(
+ context,
+ binding.ivMenu,
+ item.id,
+ item.writerId,
+ creatorId
+ ) {
+ binding.rlCommentModify.visibility = View.VISIBLE
+ binding.tvComment.visibility = View.GONE
+ }
+ }
+
+ binding.tvModify.setOnClickListener {
+ binding.rlCommentModify.visibility = View.GONE
+ binding.tvComment.visibility = View.VISIBLE
+ modifyComment(item.id, binding.etCommentModify.text.toString())
+ }
+ } else {
+ binding.ivMenu.visibility = View.GONE
+ }
+ }
+
+}
diff --git a/app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/creator_community/all/comment/CreatorCommunityCommentReplyFragment.kt b/app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/creator_community/all/comment/CreatorCommunityCommentReplyFragment.kt
new file mode 100644
index 0000000..51fb407
--- /dev/null
+++ b/app/src/main/java/kr/co/vividnext/sodalive/explorer/profile/creator_community/all/comment/CreatorCommunityCommentReplyFragment.kt
@@ -0,0 +1,239 @@
+package kr.co.vividnext.sodalive.explorer.profile.creator_community.all.comment
+
+import android.annotation.SuppressLint
+import android.app.Service
+import android.graphics.Rect
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.view.inputmethod.InputMethodManager
+import android.widget.Toast
+import androidx.core.os.BundleCompat
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+import coil.load
+import coil.transform.CircleCropTransformation
+import kr.co.vividnext.sodalive.R
+import kr.co.vividnext.sodalive.base.BaseFragment
+import kr.co.vividnext.sodalive.base.SodaDialog
+import kr.co.vividnext.sodalive.common.Constants
+import kr.co.vividnext.sodalive.common.LoadingDialog
+import kr.co.vividnext.sodalive.common.SharedPreferenceManager
+import kr.co.vividnext.sodalive.databinding.FragmentAudioContentCommentReplyBinding
+import kr.co.vividnext.sodalive.explorer.profile.creator_community.GetCommunityPostCommentListItem
+import kr.co.vividnext.sodalive.extensions.dpToPx
+import org.koin.android.ext.android.inject
+
+class CreatorCommunityCommentReplyFragment : BaseFragment(
+ FragmentAudioContentCommentReplyBinding::inflate
+) {
+ private val viewModel: CreatorCommunityCommentListViewModel by inject()
+
+ private lateinit var imm: InputMethodManager
+ private lateinit var loadingDialog: LoadingDialog
+ private lateinit var adapter: CreatorCommunityCommentReplyAdapter
+
+ private var originalComment: GetCommunityPostCommentListItem? = null
+ private var creatorId: Long = 0
+ private var postId: Long = 0
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View? {
+ creatorId = arguments?.getLong(Constants.EXTRA_COMMUNITY_CREATOR_ID) ?: 0
+ postId = arguments?.getLong(Constants.EXTRA_COMMUNITY_POST_ID) ?: 0
+ originalComment = BundleCompat.getParcelable(
+ requireArguments(),
+ Constants.EXTRA_COMMUNITY_POST_COMMENT,
+ GetCommunityPostCommentListItem::class.java
+ )
+ return super.onCreateView(inflater, container, savedInstanceState)
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ if (originalComment == null) {
+ parentFragmentManager.popBackStack()
+ }
+
+ loadingDialog = LoadingDialog(requireActivity(), layoutInflater)
+ imm = requireContext().getSystemService(
+ Service.INPUT_METHOD_SERVICE
+ ) as InputMethodManager
+
+ setupView()
+ bindData()
+ viewModel.getCommentReplyList(commentId = originalComment!!.id) {
+ parentFragmentManager.popBackStack()
+ }
+ }
+
+ private fun hideDialog() {
+ (parentFragment as CreatorCommunityCommentFragment).hideCommentDialog()
+ }
+
+ private fun setupView() {
+ binding.root.setOnClickListener { }
+
+ binding.tvBack.setOnClickListener {
+ parentFragmentManager.popBackStack()
+ }
+
+ binding.ivClose.setOnClickListener { hideDialog() }
+
+ binding.ivCommentProfile.load(SharedPreferenceManager.profileImage) {
+ crossfade(true)
+ placeholder(R.drawable.ic_place_holder)
+ transformations(CircleCropTransformation())
+ }
+
+ binding.ivCommentSend.setOnClickListener {
+ hideKeyboard()
+ val comment = binding.etComment.text.toString()
+ binding.etComment.setText("")
+ viewModel.registerComment(postId, comment, originalComment!!.id)
+ }
+
+ adapter = CreatorCommunityCommentReplyAdapter(
+ creatorId = creatorId,
+ modifyComment = { commentId, comment ->
+ hideKeyboard()
+ viewModel.modifyComment(
+ commentId = commentId,
+ postId = postId,
+ parentCommentId = originalComment!!.id,
+ comment = comment
+ )
+ },
+ onClickDelete = {
+ SodaDialog(
+ activity = requireActivity(),
+ layoutInflater = layoutInflater,
+ title = "댓글 삭제",
+ desc = "삭제하시겠습니까?",
+ confirmButtonTitle = "삭제",
+ confirmButtonClick = {
+ viewModel.modifyComment(
+ commentId = it,
+ postId = postId,
+ parentCommentId = originalComment!!.id,
+ isActive = false
+ )
+ },
+ cancelButtonTitle = "취소",
+ cancelButtonClick = {}
+ ).show(screenWidth)
+ },
+ ).apply {
+ items.add(originalComment!!)
+ }
+
+ val recyclerView = binding.rvCommentReply
+ recyclerView.setHasFixedSize(true)
+ recyclerView.layoutManager = LinearLayoutManager(
+ activity,
+ LinearLayoutManager.VERTICAL,
+ false
+ )
+
+ recyclerView.addItemDecoration(object : RecyclerView.ItemDecoration() {
+ override fun getItemOffsets(
+ outRect: Rect,
+ view: View,
+ parent: RecyclerView,
+ state: RecyclerView.State
+ ) {
+ super.getItemOffsets(outRect, view, parent, state)
+
+ outRect.left = 13.3f.dpToPx().toInt()
+ outRect.right = 13.3f.dpToPx().toInt()
+
+ when (parent.getChildAdapterPosition(view)) {
+ 0 -> {
+ outRect.top = 13.3f.dpToPx().toInt()
+ outRect.bottom = 12f.dpToPx().toInt()
+ }
+
+ adapter.itemCount - 1 -> {
+ outRect.top = 12f.dpToPx().toInt()
+ outRect.bottom = 13.3f.dpToPx().toInt()
+ }
+
+ else -> {
+ outRect.top = 12f.dpToPx().toInt()
+ outRect.bottom = 12f.dpToPx().toInt()
+ }
+ }
+ }
+ })
+
+ recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
+ override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
+ super.onScrolled(recyclerView, dx, dy)
+
+ val lastVisibleItemPosition = (recyclerView.layoutManager as LinearLayoutManager?)!!
+ .findLastCompletelyVisibleItemPosition()
+ val itemTotalCount = recyclerView.adapter!!.itemCount - 1
+
+ // 스크롤이 끝에 도달했는지 확인
+ if (!recyclerView.canScrollVertically(1) &&
+ lastVisibleItemPosition == itemTotalCount
+ ) {
+ viewModel.getCommentReplyList(originalComment!!.id)
+ }
+ }
+ })
+
+ recyclerView.adapter = adapter
+ }
+
+ @SuppressLint("NotifyDataSetChanged")
+ private fun bindData() {
+ viewModel.isLoading.observe(viewLifecycleOwner) {
+ if (it) {
+ loadingDialog.show(screenWidth)
+ } else {
+ loadingDialog.dismiss()
+ }
+ }
+
+ viewModel.toastLiveData.observe(viewLifecycleOwner) {
+ it?.let { Toast.makeText(requireContext(), it, Toast.LENGTH_LONG).show() }
+ }
+
+ viewModel.commentList.observe(viewLifecycleOwner) {
+ if (viewModel.page - 1 == 1) {
+ adapter.items.clear()
+ binding.rvCommentReply.scrollToPosition(0)
+ adapter.items.add(originalComment!!)
+ }
+
+ adapter.items.addAll(it)
+ adapter.notifyDataSetChanged()
+ }
+ }
+
+ private fun hideKeyboard() {
+ imm.hideSoftInputFromWindow(view?.windowToken, 0)
+ }
+
+ companion object {
+ fun newInstance(
+ creatorId: Long,
+ postId: Long,
+ comment: GetCommunityPostCommentListItem
+ ): CreatorCommunityCommentReplyFragment {
+ val args = Bundle()
+ args.putLong(Constants.EXTRA_COMMUNITY_POST_ID, postId)
+ args.putLong(Constants.EXTRA_COMMUNITY_CREATOR_ID, creatorId)
+ args.putParcelable(Constants.EXTRA_COMMUNITY_POST_COMMENT, comment)
+
+ val fragment = CreatorCommunityCommentReplyFragment()
+ fragment.arguments = args
+ return fragment
+ }
+ }
+}
diff --git a/app/src/main/res/drawable/bg_round_corner_5_3_222222.xml b/app/src/main/res/drawable/bg_round_corner_5_3_222222.xml
new file mode 100644
index 0000000..2989d8f
--- /dev/null
+++ b/app/src/main/res/drawable/bg_round_corner_5_3_222222.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_creator_community_all.xml b/app/src/main/res/layout/activity_creator_community_all.xml
new file mode 100644
index 0000000..7f4605d
--- /dev/null
+++ b/app/src/main/res/layout/activity_creator_community_all.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/item_community_post_comment.xml b/app/src/main/res/layout/item_community_post_comment.xml
new file mode 100644
index 0000000..bbb3f4a
--- /dev/null
+++ b/app/src/main/res/layout/item_community_post_comment.xml
@@ -0,0 +1,131 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/item_community_post_comment_reply.xml b/app/src/main/res/layout/item_community_post_comment_reply.xml
new file mode 100644
index 0000000..91af3fd
--- /dev/null
+++ b/app/src/main/res/layout/item_community_post_comment_reply.xml
@@ -0,0 +1,111 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/item_creator_community_all.xml b/app/src/main/res/layout/item_creator_community_all.xml
new file mode 100644
index 0000000..6b91ba3
--- /dev/null
+++ b/app/src/main/res/layout/item_creator_community_all.xml
@@ -0,0 +1,209 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+