커뮤니티 유료 게시글 조회, 구매 기능 추가

This commit is contained in:
2024-05-23 23:13:25 +09:00
parent a9511dcb51
commit 9d5ca7c36d
14 changed files with 376 additions and 49 deletions

View File

@@ -20,6 +20,7 @@ import androidx.appcompat.widget.PopupMenu
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import coil.load
import coil.transform.BlurTransformation
import coil.transform.CircleCropTransformation
import coil.transform.RoundedCornersTransformation
import kr.co.vividnext.sodalive.R
@@ -27,7 +28,6 @@ import kr.co.vividnext.sodalive.audio_content.AudioContentActivity
import kr.co.vividnext.sodalive.audio_content.AudioContentAdapter
import kr.co.vividnext.sodalive.audio_content.detail.AudioContentDetailActivity
import kr.co.vividnext.sodalive.audio_content.series.GetSeriesListResponse
import kr.co.vividnext.sodalive.audio_content.series.SeriesListAdapter
import kr.co.vividnext.sodalive.audio_content.series.SeriesListAllActivity
import kr.co.vividnext.sodalive.audio_content.series.detail.SeriesDetailActivity
import kr.co.vividnext.sodalive.audio_content.upload.AudioContentUploadActivity
@@ -831,6 +831,16 @@ class UserProfileActivity : BaseActivity<ActivityUserProfileBinding>(
crossfade(true)
placeholder(R.drawable.ic_place_holder)
transformations(RoundedCornersTransformation(4.7f.dpToPx()))
if (!item.existOrdered) {
transformations(
BlurTransformation(
this@UserProfileActivity,
25f,
2.5f
)
)
}
}
layout.tvLikeCount.text = "${item.likeCount}"

View File

@@ -4,6 +4,7 @@ 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.PurchasePostRequest
import kr.co.vividnext.sodalive.explorer.profile.creator_community.all.comment.CreateCommunityPostCommentRequest
import okhttp3.MultipartBody
import okhttp3.RequestBody
@@ -91,4 +92,10 @@ interface CreatorCommunityApi {
@Query("timezone") timezone: String,
@Header("Authorization") authHeader: String
): Single<ApiResponse<GetCommunityPostListResponse>>
@POST("/creator-community/purchase")
fun purchaseCommunityPost(
@Body request: PurchasePostRequest,
@Header("Authorization") authHeader: String
): Single<ApiResponse<GetCommunityPostListResponse>>
}

View File

@@ -2,6 +2,7 @@ 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.PurchasePostRequest
import kr.co.vividnext.sodalive.explorer.profile.creator_community.all.comment.CreateCommunityPostCommentRequest
import okhttp3.MultipartBody
import okhttp3.RequestBody
@@ -98,4 +99,9 @@ class CreatorCommunityRepository(private val api: CreatorCommunityApi) {
timezone = TimeZone.getDefault().id,
authHeader = token
)
fun purchaseCommunityPost(postId: Long, token: String) = api.purchaseCommunityPost(
request = PurchasePostRequest(postId = postId, timezone = TimeZone.getDefault().id),
authHeader = token
)
}

View File

@@ -9,10 +9,12 @@ data class GetCommunityPostListResponse(
@SerializedName("creatorProfileUrl") val creatorProfileUrl: String,
@SerializedName("imageUrl") val imageUrl: String?,
@SerializedName("content") val content: String,
@SerializedName("price") val price: Int,
@SerializedName("date") val date: String,
@SerializedName("isCommentAvailable") val isCommentAvailable: Boolean,
@SerializedName("isAdult") val isAdult: Boolean,
@SerializedName("isLike") var isLike: Boolean,
@SerializedName("existOrdered") val existOrdered: Boolean,
@SerializedName("likeCount") val likeCount: Int,
@SerializedName("commentCount") val commentCount: Int,
@SerializedName("firstComment") val firstComment: GetCommunityPostCommentListItem?,

View File

@@ -62,6 +62,7 @@ class CreatorCommunityAllActivity : BaseActivity<ActivityCreatorCommunityAllBind
binding.toolbar.tvBack.setOnClickListener { finish() }
adapter = CreatorCommunityAllAdapter(
screenWidth = screenWidth,
onClickLike = { viewModel.communityPostLike(it) },
writeComment = { postId, parentId, comment ->
viewModel.registerComment(
@@ -111,6 +112,18 @@ class CreatorCommunityAllActivity : BaseActivity<ActivityCreatorCommunityAllBind
reason = it
)
}.show(screenWidth)
},
onClickPurchaseContent = { postId, can, onSuccess ->
PurchaseCommunityPostDialog(
activity = this@CreatorCommunityAllActivity,
layoutInflater = layoutInflater,
can = can,
confirmButtonClick = {
viewModel.purchaseCommunityPost(postId) {
onSuccess(it)
}
}
).show(screenWidth)
}
)

View File

@@ -11,6 +11,8 @@ import android.text.style.ClickableSpan
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView
import androidx.appcompat.widget.PopupMenu
import androidx.recyclerview.widget.RecyclerView
@@ -19,17 +21,22 @@ 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.GetCommunityPostCommentListItem
import kr.co.vividnext.sodalive.explorer.profile.creator_community.GetCommunityPostListResponse
import kr.co.vividnext.sodalive.extensions.dpToPx
import kr.co.vividnext.sodalive.extensions.loadUrl
import java.util.regex.Pattern
class CreatorCommunityAllAdapter(
private val screenWidth: Int,
private val onClickLike: (Long) -> Unit,
private val writeComment: (Long, Long?, String) -> Unit,
private val showCommentBottomSheetDialog: (Long) -> Unit,
private val onClickModify: (Long) -> Unit,
private val onClickDelete: (Long) -> Unit,
private val onClickReport: (Long) -> Unit
private val onClickReport: (Long) -> Unit,
private val onClickPurchaseContent:
(Long, Int, onSuccess: (GetCommunityPostListResponse) -> Unit) -> Unit
) : RecyclerView.Adapter<CreatorCommunityAllAdapter.ViewHolder>() {
val items = mutableListOf<GetCommunityPostListResponse>()
@@ -38,7 +45,7 @@ class CreatorCommunityAllAdapter(
private val context: Context,
private val binding: ItemCreatorCommunityAllBinding
) : RecyclerView.ViewHolder(binding.root) {
@SuppressLint("NotifyDataSetChanged")
@SuppressLint("NotifyDataSetChanged", "SetTextI18n")
fun bind(item: GetCommunityPostListResponse, index: Int) {
binding.tvDate.text = item.date
binding.tvNickname.text = item.creatorNickname
@@ -48,19 +55,41 @@ class CreatorCommunityAllAdapter(
transformations(CircleCropTransformation())
}
setNoticeAndClickableUrl(binding.tvContent, item.content)
binding.tvContent.setOnClickListener {
items[index] = items[index].copy(
isExpand = !item.isExpand,
)
notifyDataSetChanged()
}
binding.tvContent.maxLines = if (item.isExpand) {
Int.MAX_VALUE
} else {
3
}
if (item.price > 0) {
if (item.existOrdered) {
setContent(item, index)
} else {
binding.llLike.visibility = View.GONE
binding.tvContent.visibility = View.GONE
binding.ivContent.visibility = View.GONE
binding.llComment.visibility = View.GONE
binding.ivSeeMore.visibility = View.GONE
binding.llLockPost.visibility = View.VISIBLE
val lockPostWidth = (screenWidth - 42f.dpToPx()).toInt()
val lp = binding.llLockPost.layoutParams as LinearLayout.LayoutParams
lp.width = lockPostWidth
lp.height = lockPostWidth
binding.llLockPost.layoutParams = lp
binding.tvPurchase.text = "${item.price}캔으로 게시글 보기"
binding.tvPurchase.setOnClickListener {
onClickPurchaseContent(item.postId, item.price) { post ->
items[index] = post
setContent(post, index)
}
}
}
} else {
setContent(item, index)
}
}
private fun setContent(item: GetCommunityPostListResponse, index: Int) {
binding.llLockPost.visibility = View.GONE
binding.llLike.visibility = View.VISIBLE
binding.llComment.visibility = View.VISIBLE
binding.ivSeeMore.visibility = View.VISIBLE
binding.ivSeeMore.setOnClickListener {
showOptionMenu(
context = context,
@@ -70,55 +99,54 @@ class CreatorCommunityAllAdapter(
)
}
binding.ivLike.setImageResource(
if (item.isLike) {
R.drawable.ic_audio_content_heart_pressed
} else {
R.drawable.ic_audio_content_heart_normal
}
setImageContent(binding.ivContent, item.imageUrl)
setContentLike(item.isLike, item.likeCount, item.postId, index)
setNoticeAndClickableUrl(binding.tvContent, item.content, item.isExpand, index)
setContentComment(
item.postId,
item.commentCount,
item.isCommentAvailable,
comment = item.firstComment
)
}
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) {
private fun setImageContent(ivContent: ImageView, imageUrl: String?) {
ivContent.visibility = View.VISIBLE
if (imageUrl != null) {
ivContent.visibility = View.VISIBLE
ivContent.loadUrl(imageUrl) {
crossfade(true)
placeholder(R.drawable.bg_placeholder)
}
} else {
binding.ivContent.visibility = View.GONE
ivContent.visibility = View.GONE
}
}
if (item.isCommentAvailable) {
private fun setContentComment(
postId: Long,
commentCount: Int,
isCommentAvailable: Boolean,
comment: GetCommunityPostCommentListItem?
) {
if (isCommentAvailable) {
binding.llComment.visibility = View.VISIBLE
binding.tvCommentCount.text = "${item.commentCount}"
binding.tvCommentCount.text = "$commentCount"
} else {
binding.llComment.visibility = View.GONE
}
if (item.commentCount > 0) {
binding.ivCommentProfile.load(item.firstComment!!.profileUrl) {
if (commentCount > 0 && comment != null) {
binding.ivCommentProfile.load(comment.profileUrl) {
crossfade(true)
placeholder(R.drawable.bg_placeholder)
transformations(CircleCropTransformation())
}
binding.tvCommentText.text = item.firstComment.comment
binding.tvCommentText.text = comment.comment
binding.tvCommentText.visibility = View.VISIBLE
binding.rlInputComment.visibility = View.GONE
binding.llComment.setOnClickListener { showCommentBottomSheetDialog(item.postId) }
binding.llComment.setOnClickListener { showCommentBottomSheetDialog(postId) }
} else {
binding.tvCommentText.visibility = View.GONE
binding.rlInputComment.visibility = View.VISIBLE
@@ -129,16 +157,45 @@ class CreatorCommunityAllAdapter(
}
binding.ivCommentSend.setOnClickListener {
val comment = binding.etComment.text.toString()
val inputComment = binding.etComment.text.toString()
binding.etComment.setText("")
writeComment(item.postId, null, comment)
writeComment(postId, null, inputComment)
}
binding.llComment.setOnClickListener {}
}
}
private fun setNoticeAndClickableUrl(textView: TextView, text: String) {
@SuppressLint("NotifyDataSetChanged")
private fun setContentLike(isLike: Boolean, likeCount: Int, postId: Long, index: Int) {
binding.ivLike.setImageResource(
if (isLike) {
R.drawable.ic_audio_content_heart_pressed
} else {
R.drawable.ic_audio_content_heart_normal
}
)
binding.tvLike.text = "$likeCount"
binding.llLike.setOnClickListener {
items[index] = items[index].copy(
isLike = !isLike,
likeCount = if (!isLike) likeCount + 1 else likeCount - 1
)
notifyDataSetChanged()
onClickLike(postId)
}
}
@SuppressLint("NotifyDataSetChanged")
private fun setNoticeAndClickableUrl(
textView: TextView,
text: String,
isExpand: Boolean,
index: Int
) {
textView.visibility = View.VISIBLE
textView.text = text
val spannable = SpannableString(text)
@@ -159,6 +216,19 @@ class CreatorCommunityAllAdapter(
textView.text = spannable
textView.movementMethod = LinkMovementMethod.getInstance()
textView.setOnClickListener {
items[index] = items[index].copy(
isExpand = !isExpand,
)
notifyDataSetChanged()
}
textView.maxLines = if (isExpand) {
Int.MAX_VALUE
} else {
3
}
}
}

View File

@@ -220,4 +220,41 @@ class CreatorCommunityAllViewModel(
)
)
}
fun purchaseCommunityPost(postId: Long, onSuccess: (GetCommunityPostListResponse) -> Unit) {
if (!_isLoading.value!!) {
_isLoading.value = true
compositeDisposable.add(
repository
.purchaseCommunityPost(
postId = postId,
token = "Bearer ${SharedPreferenceManager.token}"
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
if (it.success && it.data != null) {
onSuccess(it.data)
} 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("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
}
)
)
}
}
}

View File

@@ -0,0 +1,51 @@
package kr.co.vividnext.sodalive.explorer.profile.creator_community.all
import android.annotation.SuppressLint
import android.app.Activity
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.view.LayoutInflater
import android.view.WindowManager
import androidx.appcompat.app.AlertDialog
import kr.co.vividnext.sodalive.databinding.DialogPurchaseCommunityPostBinding
import kr.co.vividnext.sodalive.extensions.dpToPx
import kr.co.vividnext.sodalive.extensions.moneyFormat
@SuppressLint("SetTextI18n")
class PurchaseCommunityPostDialog(
activity: Activity,
layoutInflater: LayoutInflater,
can: Int,
confirmButtonClick: () -> Unit,
) {
private val alertDialog: AlertDialog
val dialogView = DialogPurchaseCommunityPostBinding.inflate(layoutInflater)
init {
val dialogBuilder = AlertDialog.Builder(activity)
dialogBuilder.setView(dialogView.root)
alertDialog = dialogBuilder.create()
alertDialog.setCancelable(false)
alertDialog.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
dialogView.tvCan.text = "${can.moneyFormat()}캔으로 보기"
dialogView.tvCan.setOnClickListener {
alertDialog.dismiss()
confirmButtonClick()
}
dialogView.tvCancel.setOnClickListener { alertDialog.dismiss() }
}
fun show(width: Int) {
alertDialog.show()
val lp = WindowManager.LayoutParams()
lp.copyFrom(alertDialog.window?.attributes)
lp.width = width - (26.7f.dpToPx()).toInt()
lp.height = WindowManager.LayoutParams.WRAP_CONTENT
alertDialog.window?.attributes = lp
}
}

View File

@@ -0,0 +1,9 @@
package kr.co.vividnext.sodalive.explorer.profile.creator_community.all
import com.google.gson.annotations.SerializedName
data class PurchasePostRequest(
@SerializedName("postId") val postId: Long,
@SerializedName("timezone") val timezone: String,
@SerializedName("container") val container: String = "aos",
)