feat(creator): 채널 후원 버튼을 연결한다

This commit is contained in:
2026-06-16 19:22:28 +09:00
parent de351d700c
commit 28433c10df
4 changed files with 149 additions and 11 deletions

View File

@@ -3,6 +3,7 @@ package kr.co.vividnext.sodalive.v2.creator.channel
import android.content.Context
import android.content.Intent
import android.graphics.Color
import android.view.LayoutInflater
import android.view.View
import android.view.View.MeasureSpec
import android.widget.LinearLayout
@@ -26,6 +27,7 @@ import kr.co.vividnext.sodalive.extensions.dpToPx
import kr.co.vividnext.sodalive.extensions.loadUrl
import kr.co.vividnext.sodalive.extensions.moneyFormat
import kr.co.vividnext.sodalive.live.room.detail.LiveRoomDetailFragment
import kr.co.vividnext.sodalive.live.room.donation.LiveRoomDonationDialog
import kr.co.vividnext.sodalive.report.ProfileReportDialog
import kr.co.vividnext.sodalive.report.UserReportDialog
import kr.co.vividnext.sodalive.v2.common.CreatorActivityType
@@ -302,6 +304,23 @@ class CreatorChannelActivity :
updateViewPagerHeight()
}
override fun onCreatorChannelDonationClicked() {
val header = currentHeader ?: return
if (header.isOwner) return
val dialog = LiveRoomDonationDialog(
this,
LayoutInflater.from(this),
isLiveDonation = true,
messageMaxLength = 100,
secretToggleLabelResId = R.string.screen_user_profile_channel_donation_secret,
applySecretMissionMessageHint = false
) { can, message, isSecret ->
homeActionDelegate?.postChannelDonation(can = can, isSecret = isSecret, message = message)
}
dialog.show(screenWidth - 26.7f.dpToPx().toInt())
}
private fun updateViewPagerHeight() {
binding.viewPager.post {
val recyclerView = binding.viewPager.getChildAt(0) as? RecyclerView ?: return@post

View File

@@ -18,7 +18,11 @@ class CreatorChannelHomeFragment : BaseFragment<FragmentCreatorChannelHomeBindin
) {
private val viewModel: CreatorChannelHomeViewModel by viewModel()
private val sectionAdapter = CreatorChannelHomeSectionAdapter(::onScheduleClicked, ::onAudioContentClicked)
private val sectionAdapter = CreatorChannelHomeSectionAdapter(
onScheduleClick = ::onScheduleClicked,
onAudioContentClick = ::onAudioContentClicked,
onDonationClick = ::onDonationClicked
)
private val creatorId: Long by lazy { arguments?.getLong(ARG_CREATOR_ID) ?: 0L }
private val host: Host
get() = requireActivity() as Host
@@ -49,6 +53,10 @@ class CreatorChannelHomeFragment : BaseFragment<FragmentCreatorChannelHomeBindin
override fun reportProfile() {
viewModel.reportProfile()
}
override fun postChannelDonation(can: Int, isSecret: Boolean, message: String) {
viewModel.postChannelDonation(can = can, isSecret = isSecret, message = message)
}
}
)
if (creatorId > 0L) {
@@ -97,6 +105,10 @@ class CreatorChannelHomeFragment : BaseFragment<FragmentCreatorChannelHomeBindin
host.onCreatorChannelAudioContentClicked(audioContent)
}
private fun onDonationClicked() {
host.onCreatorChannelDonationClicked()
}
interface Host {
fun onCreatorChannelHeaderChanged(header: CreatorChannelHeaderUiModel)
fun onCreatorChannelFollowProgressChanged(inProgress: Boolean)
@@ -105,6 +117,7 @@ class CreatorChannelHomeFragment : BaseFragment<FragmentCreatorChannelHomeBindin
fun onCreatorChannelAudioContentClicked(audioContent: CreatorChannelAudioContentResponse)
fun onCreatorChannelHomeActionDelegateReady(delegate: HomeActionDelegate?)
fun onCreatorChannelHomeContentChanged()
fun onCreatorChannelDonationClicked()
}
interface HomeActionDelegate {
@@ -113,6 +126,7 @@ class CreatorChannelHomeFragment : BaseFragment<FragmentCreatorChannelHomeBindin
fun blockUser()
fun reportUser(reason: String)
fun reportProfile()
fun postChannelDonation(can: Int, isSecret: Boolean, message: String)
}
companion object {

View File

@@ -17,6 +17,7 @@ import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import kr.co.vividnext.sodalive.R
import coil.transform.CircleCropTransformation
import kr.co.vividnext.sodalive.common.formatUtcRelativeTimeText
import kr.co.vividnext.sodalive.common.image.BlurTransformation
import kr.co.vividnext.sodalive.extensions.loadUrl
import kr.co.vividnext.sodalive.extensions.moneyFormat
@@ -36,7 +37,8 @@ import kotlin.math.roundToInt
class CreatorChannelHomeSectionAdapter(
private val onScheduleClick: (CreatorChannelScheduleResponse) -> Unit = {},
private val onAudioContentClick: (CreatorChannelAudioContentResponse) -> Unit = {},
private val onSeriesClick: (CreatorChannelSeriesResponse) -> Unit = {}
private val onSeriesClick: (CreatorChannelSeriesResponse) -> Unit = {},
private val onDonationClick: () -> Unit = {}
) : RecyclerView.Adapter<CreatorChannelHomeSectionAdapter.SectionViewHolder>() {
private var items: List<CreatorChannelHomeSection> = emptyList()
@@ -50,7 +52,7 @@ class CreatorChannelHomeSectionAdapter(
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SectionViewHolder {
val view = LayoutInflater.from(parent.context).inflate(viewType, parent, false)
return SectionViewHolder(view, onScheduleClick, onAudioContentClick, onSeriesClick)
return SectionViewHolder(view, onScheduleClick, onAudioContentClick, onSeriesClick, onDonationClick)
}
override fun onBindViewHolder(holder: SectionViewHolder, position: Int) {
@@ -63,7 +65,8 @@ class CreatorChannelHomeSectionAdapter(
view: View,
private val onScheduleClick: (CreatorChannelScheduleResponse) -> Unit,
private val onAudioContentClick: (CreatorChannelAudioContentResponse) -> Unit,
private val onSeriesClick: (CreatorChannelSeriesResponse) -> Unit
private val onSeriesClick: (CreatorChannelSeriesResponse) -> Unit,
private val onDonationClick: () -> Unit
) : RecyclerView.ViewHolder(view) {
private val title: TextView? = view.findViewById(R.id.tv_section_title)
private val currentLiveTitle: TextView? = view.findViewById(R.id.tv_current_live_title)
@@ -75,7 +78,12 @@ class CreatorChannelHomeSectionAdapter(
private val latestAudioPointTag: ImageView? = view.findViewById(R.id.iv_latest_audio_point_tag)
private val latestAudioTitle: TextView? = view.findViewById(R.id.tv_latest_audio_title)
private val latestAudioDuration: TextView? = view.findViewById(R.id.tv_latest_audio_duration)
private val donationItemsScrollView: View? = view.findViewById(R.id.hsv_donation_items)
private val donationItems: LinearLayout? = view.findViewById(R.id.ll_donation_items)
private val donationEmpty: View? = view.findViewById(R.id.layout_donation_empty)
private val donationEmptyButton: View? = view.findViewById(R.id.layout_donation_empty_button)
private val donationButton: View? = view.findViewById(R.id.layout_donation_button)
private val donationEmptyTitle: TextView? = view.findViewById(R.id.tv_donation_empty_title)
private val noticeItems: LinearLayout? = view.findViewById(R.id.ll_notice_items)
private val scheduleTimeline: LinearLayout? = view.findViewById(R.id.ll_schedule_timeline)
private val scheduleItems: LinearLayout? = view.findViewById(R.id.ll_schedule_items)
@@ -107,6 +115,7 @@ class CreatorChannelHomeSectionAdapter(
}
fun bind(item: CreatorChannelHomeSection) {
itemView.setOnClickListener(null)
title?.setText(item.titleResId)
donationItems?.removeAllViews()
noticeItems?.removeAllViews()
@@ -144,9 +153,26 @@ class CreatorChannelHomeSectionAdapter(
latestAudioDuration?.text = item.audioContent.duration.orEmpty()
latestAudioPointTag?.isVisible = item.audioContent.isPointAvailable
latestAudioThumbnail?.loadUrl(item.audioContent.imageUrl)
itemView.setOnClickListener { onAudioContentClick(item.audioContent) }
}
private fun bindDonations(item: CreatorChannelHomeSection.Donations) {
donationItems?.removeAllViews()
donationItemsScrollView?.isVisible = item.donations.isNotEmpty()
donationEmpty?.isVisible = item.donations.isEmpty()
val isDonationButtonVisible = item.donations.isNotEmpty() && !item.isOwner
val isDonationEmptyButtonVisible = !item.isOwner
donationButton?.isVisible = isDonationButtonVisible
donationEmptyButton?.isVisible = isDonationEmptyButtonVisible
donationEmptyTitle?.setText(
if (item.isOwner) {
R.string.creator_channel_donation_empty_owner_title
} else {
R.string.creator_channel_donation_empty_title
}
)
donationButton?.setOnClickListener(if (isDonationButtonVisible) View.OnClickListener { onDonationClick() } else null)
donationEmptyButton?.setOnClickListener(if (isDonationEmptyButtonVisible) View.OnClickListener { onDonationClick() } else null)
val visibleDonations = item.donations.take(MAX_DONATION_ITEM_COUNT)
visibleDonations.forEachIndexed { index, donation ->
val row = LayoutInflater.from(itemView.context).inflate(
@@ -167,7 +193,8 @@ class CreatorChannelHomeSectionAdapter(
transformations(CircleCropTransformation())
}
row.findViewById<TextView>(R.id.tv_donation_nickname).text = donation.nickname
row.findViewById<TextView>(R.id.tv_donation_created_at).text = donation.createdAtUtc
row.findViewById<TextView>(R.id.tv_donation_created_at).text =
formatUtcRelativeTimeText(itemView.context, donation.createdAtUtc)
row.findViewById<TextView>(R.id.tv_donation_can).text = itemView.context.getString(
R.string.creator_channel_donation_can_format,
donation.can.moneyFormat()
@@ -198,7 +225,8 @@ class CreatorChannelHomeSectionAdapter(
transformations(CircleCropTransformation())
}
row.findViewById<TextView>(R.id.tv_notice_creator_name).text = notice.creatorNickname
row.findViewById<TextView>(R.id.tv_notice_created_at).text = notice.dateUtc
row.findViewById<TextView>(R.id.tv_notice_created_at).text =
formatUtcRelativeTimeText(itemView.context, notice.dateUtc)
row.findViewById<TextView>(R.id.tv_notice_content).text = notice.content
val noticeThumbnail = row.findViewById<ImageView>(R.id.iv_notice_thumbnail)
noticeThumbnail.isVisible = !notice.imageUrl.isNullOrBlank()
@@ -383,7 +411,7 @@ class CreatorChannelHomeSectionAdapter(
postId = postId.toString(),
bodyText = content,
keywordText = "",
createdAtText = dateUtc,
createdAtText = formatUtcRelativeTimeText(itemView.context, dateUtc),
commentCount = commentCount,
likeCount = likeCount,
imageUrl = imageUrl,