feat(creator-channel): 홈 섹션 이동을 연결한다

This commit is contained in:
2026-07-09 08:19:00 +09:00
parent b745c3d675
commit f3a4578a66
4 changed files with 124 additions and 4 deletions

View File

@@ -546,6 +546,11 @@ class CreatorChannelActivity :
updateViewPagerHeight()
}
override fun onCreatorChannelHomeTabRequested(tab: CreatorChannelTab) {
selectCreatorChannelTab(tab)
scrollCreatorChannelToTop()
}
override fun onCreatorChannelLiveContentChanged() {
updateCreatorChannelTabViewportHeight()
updateViewPagerHeight {
@@ -1184,6 +1189,14 @@ class CreatorChannelActivity :
)
}
private fun selectCreatorChannelTab(tab: CreatorChannelTab) {
binding.viewPager.currentItem = tab.ordinal
}
private fun scrollCreatorChannelToTop() {
binding.nestedScrollView.scrollTo(0, 0)
}
override fun onResume() {
super.onResume()
binding.btnCreatorChannelOwnerCta.isEnabled = true

View File

@@ -12,6 +12,7 @@ import kr.co.vividnext.sodalive.v2.creator.channel.data.CreatorChannelScheduleRe
import kr.co.vividnext.sodalive.v2.creator.channel.data.CreatorChannelSeriesResponse
import kr.co.vividnext.sodalive.v2.creator.channel.model.CreatorChannelHeaderUiModel
import kr.co.vividnext.sodalive.v2.creator.channel.model.CreatorChannelHomeUiState
import kr.co.vividnext.sodalive.v2.creator.channel.model.CreatorChannelTab
import kr.co.vividnext.sodalive.v2.creator.channel.ui.CreatorChannelHomeSectionAdapter
import org.koin.androidx.viewmodel.ext.android.viewModel
@@ -25,7 +26,9 @@ class CreatorChannelHomeFragment : BaseFragment<FragmentCreatorChannelHomeBindin
onScheduleClick = ::onScheduleClicked,
onAudioContentClick = ::onAudioContentClicked,
onSeriesClick = ::onSeriesClicked,
onDonationClick = ::onDonationClicked
onDonationClick = ::onDonationClicked,
onSectionChevronClick = ::onSectionChevronClicked,
onCommunityClick = ::onCommunityClicked
)
private val creatorId: Long by lazy { arguments?.getLong(ARG_CREATOR_ID) ?: 0L }
private val host: Host
@@ -123,6 +126,14 @@ class CreatorChannelHomeFragment : BaseFragment<FragmentCreatorChannelHomeBindin
host.onCreatorChannelDonationClicked()
}
private fun onSectionChevronClicked(tab: CreatorChannelTab) {
host.onCreatorChannelHomeTabRequested(tab)
}
private fun onCommunityClicked(postId: Long) {
host.onCreatorChannelCommunityPostClicked(postId)
}
private fun onCurrentLiveClicked(live: CreatorChannelLiveResponse) {
host.onCreatorChannelCurrentLiveClicked(live)
}
@@ -137,6 +148,8 @@ class CreatorChannelHomeFragment : BaseFragment<FragmentCreatorChannelHomeBindin
fun onCreatorChannelHomeActionDelegateReady(delegate: HomeActionDelegate?)
fun onCreatorChannelHomeContentChanged()
fun onCreatorChannelDonationClicked()
fun onCreatorChannelHomeTabRequested(tab: CreatorChannelTab)
fun onCreatorChannelCommunityPostClicked(postId: Long)
fun onCreatorChannelCurrentLiveClicked(live: CreatorChannelLiveResponse)
}

View File

@@ -26,6 +26,7 @@ import kr.co.vividnext.sodalive.v2.creator.channel.data.CreatorChannelLiveRespon
import kr.co.vividnext.sodalive.v2.creator.channel.data.CreatorChannelScheduleResponse
import kr.co.vividnext.sodalive.v2.creator.channel.data.CreatorChannelSeriesResponse
import kr.co.vividnext.sodalive.v2.creator.channel.model.CreatorChannelHomeSection
import kr.co.vividnext.sodalive.v2.creator.channel.model.CreatorChannelTab
import kr.co.vividnext.sodalive.v2.widget.feed.FeedCommunityView
import kr.co.vividnext.sodalive.v2.widget.feed.FeedItem
import kr.co.vividnext.sodalive.v2.widget.feed.FeedSize
@@ -39,7 +40,9 @@ class CreatorChannelHomeSectionAdapter(
private val onScheduleClick: (CreatorChannelScheduleResponse) -> Unit = {},
private val onAudioContentClick: (CreatorChannelAudioContentResponse) -> Unit = {},
private val onSeriesClick: (CreatorChannelSeriesResponse) -> Unit = {},
private val onDonationClick: () -> Unit = {}
private val onDonationClick: () -> Unit = {},
private val onSectionChevronClick: (CreatorChannelTab) -> Unit = {},
private val onCommunityClick: (Long) -> Unit = {}
) : RecyclerView.Adapter<CreatorChannelHomeSectionAdapter.SectionViewHolder>() {
private var items: List<CreatorChannelHomeSection> = emptyList()
@@ -53,7 +56,16 @@ class CreatorChannelHomeSectionAdapter(
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SectionViewHolder {
val view = LayoutInflater.from(parent.context).inflate(viewType, parent, false)
return SectionViewHolder(view, onLiveClick, onScheduleClick, onAudioContentClick, onSeriesClick, onDonationClick)
return SectionViewHolder(
view,
onLiveClick,
onScheduleClick,
onAudioContentClick,
onSeriesClick,
onDonationClick,
onSectionChevronClick,
onCommunityClick
)
}
override fun onBindViewHolder(holder: SectionViewHolder, position: Int) {
@@ -68,9 +80,12 @@ class CreatorChannelHomeSectionAdapter(
private val onScheduleClick: (CreatorChannelScheduleResponse) -> Unit,
private val onAudioContentClick: (CreatorChannelAudioContentResponse) -> Unit,
private val onSeriesClick: (CreatorChannelSeriesResponse) -> Unit,
private val onDonationClick: () -> Unit
private val onDonationClick: () -> Unit,
private val onSectionChevronClick: (CreatorChannelTab) -> Unit,
private val onCommunityClick: (Long) -> Unit
) : RecyclerView.ViewHolder(view) {
private val title: TextView? = view.findViewById(R.id.tv_section_title)
private val sectionTitleChevron: ImageView? = view.findViewById(R.id.iv_section_title_chevron)
private val currentLiveTitle: TextView? = view.findViewById(R.id.tv_current_live_title)
private val currentLiveStartTime: TextView? = view.findViewById(R.id.tv_current_live_start_time)
private val currentLivePrice: TextView? = view.findViewById(R.id.tv_current_live_price)
@@ -93,6 +108,7 @@ class CreatorChannelHomeSectionAdapter(
private val audioContentsRecyclerView: RecyclerView? = view.findViewById(R.id.rv_audio_contents)
private val seriesItems: LinearLayout? = view.findViewById(R.id.ll_series_items)
private val communityItems: LinearLayout? = view.findViewById(R.id.ll_community_items)
private val communityMoreButton: View? = view.findViewById(R.id.layout_community_more_button)
private val fanTalkCard: View? = view.findViewById(R.id.layout_fantalk_card)
private val fanTalkTotalRow: View? = view.findViewById(R.id.layout_fantalk_total_row)
private val fanTalkLatestRow: View? = view.findViewById(R.id.layout_fantalk_latest_row)
@@ -120,6 +136,11 @@ class CreatorChannelHomeSectionAdapter(
fun bind(item: CreatorChannelHomeSection) {
itemView.setOnClickListener(null)
title?.setText(item.titleResId)
sectionTitleChevron?.isVisible = item.chevronTab != null
sectionTitleChevron?.setOnClickListener {
item.chevronTab?.let(onSectionChevronClick)
}
communityMoreButton?.setOnClickListener(null)
donationItems?.removeAllViews()
noticeItems?.removeAllViews()
scheduleTimeline?.removeAllViews()
@@ -351,6 +372,7 @@ class CreatorChannelHomeSectionAdapter(
}
private fun bindCommunities(item: CreatorChannelHomeSection.Communities) {
communityMoreButton?.setOnClickListener { onSectionChevronClick(CreatorChannelTab.Community) }
val visibleCommunities = item.communities.take(MAX_COMMUNITY_ITEM_COUNT)
visibleCommunities.forEachIndexed { index, community ->
val communityWidthDp = calculateCreatorChannelCommunityCardWidthDp(
@@ -377,6 +399,7 @@ class CreatorChannelHomeSectionAdapter(
).apply {
bottomMargin = if (index == visibleCommunities.lastIndex) 0 else 8.dp()
}
row.setOnClickListener { onCommunityClick(community.postId) }
communityItems?.addView(row)
}
}
@@ -590,6 +613,15 @@ class CreatorChannelHomeSectionAdapter(
is CreatorChannelHomeSection.Sns -> R.string.creator_channel_section_sns
}
val CreatorChannelHomeSection.chevronTab: CreatorChannelTab?
get() = when (this) {
is CreatorChannelHomeSection.Donations -> CreatorChannelTab.Donation
is CreatorChannelHomeSection.AudioContents -> CreatorChannelTab.Audio
is CreatorChannelHomeSection.Series -> CreatorChannelTab.Series
is CreatorChannelHomeSection.FanTalk -> CreatorChannelTab.FanTalk
else -> null
}
private const val MAX_DONATION_ITEM_COUNT = 8
private const val MAX_NOTICE_ITEM_COUNT = 3
private const val MAX_SCHEDULE_ITEM_COUNT = 3

View File

@@ -980,6 +980,68 @@ class CreatorChannelActivitySourceTest {
assertTrue(activity.contains("dialog.show(screenWidth - 26.7f.dpToPx().toInt())"))
}
@Test
fun `홈 섹션 chevron과 커뮤니티 전체보기는 대응 탭으로 이동한다`() {
val adapter = projectFile(
"app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/ui/CreatorChannelHomeSectionAdapter.kt"
).readText()
val fragment = projectFile(
"app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelHomeFragment.kt"
).readText()
val activity = projectFile(
"app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt"
).readText()
assertTrue(adapter.contains("private val sectionTitleChevron: ImageView?"))
assertTrue(adapter.contains("private val communityMoreButton: View?"))
assertTrue(adapter.contains("sectionTitleChevron?.isVisible = item.chevronTab != null"))
assertTrue(adapter.contains("sectionTitleChevron?.setOnClickListener"))
assertTrue(adapter.contains("item.chevronTab?.let(onSectionChevronClick)"))
assertTrue(
adapter.contains(
"communityMoreButton?.setOnClickListener { onSectionChevronClick(CreatorChannelTab.Community) }"
)
)
assertTrue(adapter.contains("is CreatorChannelHomeSection.Donations -> CreatorChannelTab.Donation"))
assertTrue(adapter.contains("is CreatorChannelHomeSection.AudioContents -> CreatorChannelTab.Audio"))
assertTrue(adapter.contains("is CreatorChannelHomeSection.Series -> CreatorChannelTab.Series"))
assertTrue(adapter.contains("is CreatorChannelHomeSection.FanTalk -> CreatorChannelTab.FanTalk"))
assertTrue(fragment.contains("onSectionChevronClick = ::onSectionChevronClicked"))
assertTrue(fragment.contains("host.onCreatorChannelHomeTabRequested(tab)"))
assertTrue(activity.contains("override fun onCreatorChannelHomeTabRequested(tab: CreatorChannelTab)"))
assertTrue(activity.contains("private fun selectCreatorChannelTab(tab: CreatorChannelTab)"))
assertTrue(activity.contains("binding.viewPager.currentItem = tab.ordinal"))
assertTrue(activity.contains("private fun scrollCreatorChannelToTop()"))
assertTrue(activity.contains("binding.nestedScrollView.scrollTo(0, 0)"))
assertTrue(
activity.contains(
"override fun onCreatorChannelHomeTabRequested(tab: CreatorChannelTab) {\n" +
" selectCreatorChannelTab(tab)\n" +
" scrollCreatorChannelToTop()"
)
)
}
@Test
fun `홈 커뮤니티 아이템 클릭은 커뮤니티 상세 이동 host 계약을 사용한다`() {
val adapter = projectFile(
"app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/ui/CreatorChannelHomeSectionAdapter.kt"
).readText()
val fragment = projectFile(
"app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelHomeFragment.kt"
).readText()
val activity = projectFile(
"app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt"
).readText()
assertTrue(adapter.contains("private val onCommunityClick: (Long) -> Unit = {}"))
assertTrue(adapter.contains("row.setOnClickListener { onCommunityClick(community.postId) }"))
assertTrue(fragment.contains("onCommunityClick = ::onCommunityClicked"))
assertTrue(fragment.contains("host.onCreatorChannelCommunityPostClicked(postId)"))
assertTrue(activity.contains("override fun onCreatorChannelCommunityPostClicked(postId: Long)"))
assertTrue(activity.contains("CreatorChannelCommunityDetailActivity.newIntent(this, postId)"))
}
@Test
fun `후원 can 수량은 요청된 배경색 resource 경계로 매핑한다`() {
assertEquals(R.color.gray_200, calculateCreatorChannelDonationHeaderColorRes(1))