feat(creator-channel): 홈 섹션 이동을 연결한다
This commit is contained in:
@@ -546,6 +546,11 @@ class CreatorChannelActivity :
|
|||||||
updateViewPagerHeight()
|
updateViewPagerHeight()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onCreatorChannelHomeTabRequested(tab: CreatorChannelTab) {
|
||||||
|
selectCreatorChannelTab(tab)
|
||||||
|
scrollCreatorChannelToTop()
|
||||||
|
}
|
||||||
|
|
||||||
override fun onCreatorChannelLiveContentChanged() {
|
override fun onCreatorChannelLiveContentChanged() {
|
||||||
updateCreatorChannelTabViewportHeight()
|
updateCreatorChannelTabViewportHeight()
|
||||||
updateViewPagerHeight {
|
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() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
binding.btnCreatorChannelOwnerCta.isEnabled = true
|
binding.btnCreatorChannelOwnerCta.isEnabled = true
|
||||||
|
|||||||
@@ -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.data.CreatorChannelSeriesResponse
|
||||||
import kr.co.vividnext.sodalive.v2.creator.channel.model.CreatorChannelHeaderUiModel
|
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.CreatorChannelHomeUiState
|
||||||
|
import kr.co.vividnext.sodalive.v2.creator.channel.model.CreatorChannelTab
|
||||||
import kr.co.vividnext.sodalive.v2.creator.channel.ui.CreatorChannelHomeSectionAdapter
|
import kr.co.vividnext.sodalive.v2.creator.channel.ui.CreatorChannelHomeSectionAdapter
|
||||||
import org.koin.androidx.viewmodel.ext.android.viewModel
|
import org.koin.androidx.viewmodel.ext.android.viewModel
|
||||||
|
|
||||||
@@ -25,7 +26,9 @@ class CreatorChannelHomeFragment : BaseFragment<FragmentCreatorChannelHomeBindin
|
|||||||
onScheduleClick = ::onScheduleClicked,
|
onScheduleClick = ::onScheduleClicked,
|
||||||
onAudioContentClick = ::onAudioContentClicked,
|
onAudioContentClick = ::onAudioContentClicked,
|
||||||
onSeriesClick = ::onSeriesClicked,
|
onSeriesClick = ::onSeriesClicked,
|
||||||
onDonationClick = ::onDonationClicked
|
onDonationClick = ::onDonationClicked,
|
||||||
|
onSectionChevronClick = ::onSectionChevronClicked,
|
||||||
|
onCommunityClick = ::onCommunityClicked
|
||||||
)
|
)
|
||||||
private val creatorId: Long by lazy { arguments?.getLong(ARG_CREATOR_ID) ?: 0L }
|
private val creatorId: Long by lazy { arguments?.getLong(ARG_CREATOR_ID) ?: 0L }
|
||||||
private val host: Host
|
private val host: Host
|
||||||
@@ -123,6 +126,14 @@ class CreatorChannelHomeFragment : BaseFragment<FragmentCreatorChannelHomeBindin
|
|||||||
host.onCreatorChannelDonationClicked()
|
host.onCreatorChannelDonationClicked()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun onSectionChevronClicked(tab: CreatorChannelTab) {
|
||||||
|
host.onCreatorChannelHomeTabRequested(tab)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun onCommunityClicked(postId: Long) {
|
||||||
|
host.onCreatorChannelCommunityPostClicked(postId)
|
||||||
|
}
|
||||||
|
|
||||||
private fun onCurrentLiveClicked(live: CreatorChannelLiveResponse) {
|
private fun onCurrentLiveClicked(live: CreatorChannelLiveResponse) {
|
||||||
host.onCreatorChannelCurrentLiveClicked(live)
|
host.onCreatorChannelCurrentLiveClicked(live)
|
||||||
}
|
}
|
||||||
@@ -137,6 +148,8 @@ class CreatorChannelHomeFragment : BaseFragment<FragmentCreatorChannelHomeBindin
|
|||||||
fun onCreatorChannelHomeActionDelegateReady(delegate: HomeActionDelegate?)
|
fun onCreatorChannelHomeActionDelegateReady(delegate: HomeActionDelegate?)
|
||||||
fun onCreatorChannelHomeContentChanged()
|
fun onCreatorChannelHomeContentChanged()
|
||||||
fun onCreatorChannelDonationClicked()
|
fun onCreatorChannelDonationClicked()
|
||||||
|
fun onCreatorChannelHomeTabRequested(tab: CreatorChannelTab)
|
||||||
|
fun onCreatorChannelCommunityPostClicked(postId: Long)
|
||||||
fun onCreatorChannelCurrentLiveClicked(live: CreatorChannelLiveResponse)
|
fun onCreatorChannelCurrentLiveClicked(live: CreatorChannelLiveResponse)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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.CreatorChannelScheduleResponse
|
||||||
import kr.co.vividnext.sodalive.v2.creator.channel.data.CreatorChannelSeriesResponse
|
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.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.FeedCommunityView
|
||||||
import kr.co.vividnext.sodalive.v2.widget.feed.FeedItem
|
import kr.co.vividnext.sodalive.v2.widget.feed.FeedItem
|
||||||
import kr.co.vividnext.sodalive.v2.widget.feed.FeedSize
|
import kr.co.vividnext.sodalive.v2.widget.feed.FeedSize
|
||||||
@@ -39,7 +40,9 @@ class CreatorChannelHomeSectionAdapter(
|
|||||||
private val onScheduleClick: (CreatorChannelScheduleResponse) -> Unit = {},
|
private val onScheduleClick: (CreatorChannelScheduleResponse) -> Unit = {},
|
||||||
private val onAudioContentClick: (CreatorChannelAudioContentResponse) -> Unit = {},
|
private val onAudioContentClick: (CreatorChannelAudioContentResponse) -> Unit = {},
|
||||||
private val onSeriesClick: (CreatorChannelSeriesResponse) -> 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>() {
|
) : RecyclerView.Adapter<CreatorChannelHomeSectionAdapter.SectionViewHolder>() {
|
||||||
|
|
||||||
private var items: List<CreatorChannelHomeSection> = emptyList()
|
private var items: List<CreatorChannelHomeSection> = emptyList()
|
||||||
@@ -53,7 +56,16 @@ class CreatorChannelHomeSectionAdapter(
|
|||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SectionViewHolder {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SectionViewHolder {
|
||||||
val view = LayoutInflater.from(parent.context).inflate(viewType, parent, false)
|
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) {
|
override fun onBindViewHolder(holder: SectionViewHolder, position: Int) {
|
||||||
@@ -68,9 +80,12 @@ class CreatorChannelHomeSectionAdapter(
|
|||||||
private val onScheduleClick: (CreatorChannelScheduleResponse) -> Unit,
|
private val onScheduleClick: (CreatorChannelScheduleResponse) -> Unit,
|
||||||
private val onAudioContentClick: (CreatorChannelAudioContentResponse) -> Unit,
|
private val onAudioContentClick: (CreatorChannelAudioContentResponse) -> Unit,
|
||||||
private val onSeriesClick: (CreatorChannelSeriesResponse) -> 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) {
|
) : RecyclerView.ViewHolder(view) {
|
||||||
private val title: TextView? = view.findViewById(R.id.tv_section_title)
|
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 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 currentLiveStartTime: TextView? = view.findViewById(R.id.tv_current_live_start_time)
|
||||||
private val currentLivePrice: TextView? = view.findViewById(R.id.tv_current_live_price)
|
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 audioContentsRecyclerView: RecyclerView? = view.findViewById(R.id.rv_audio_contents)
|
||||||
private val seriesItems: LinearLayout? = view.findViewById(R.id.ll_series_items)
|
private val seriesItems: LinearLayout? = view.findViewById(R.id.ll_series_items)
|
||||||
private val communityItems: LinearLayout? = view.findViewById(R.id.ll_community_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 fanTalkCard: View? = view.findViewById(R.id.layout_fantalk_card)
|
||||||
private val fanTalkTotalRow: View? = view.findViewById(R.id.layout_fantalk_total_row)
|
private val fanTalkTotalRow: View? = view.findViewById(R.id.layout_fantalk_total_row)
|
||||||
private val fanTalkLatestRow: View? = view.findViewById(R.id.layout_fantalk_latest_row)
|
private val fanTalkLatestRow: View? = view.findViewById(R.id.layout_fantalk_latest_row)
|
||||||
@@ -120,6 +136,11 @@ class CreatorChannelHomeSectionAdapter(
|
|||||||
fun bind(item: CreatorChannelHomeSection) {
|
fun bind(item: CreatorChannelHomeSection) {
|
||||||
itemView.setOnClickListener(null)
|
itemView.setOnClickListener(null)
|
||||||
title?.setText(item.titleResId)
|
title?.setText(item.titleResId)
|
||||||
|
sectionTitleChevron?.isVisible = item.chevronTab != null
|
||||||
|
sectionTitleChevron?.setOnClickListener {
|
||||||
|
item.chevronTab?.let(onSectionChevronClick)
|
||||||
|
}
|
||||||
|
communityMoreButton?.setOnClickListener(null)
|
||||||
donationItems?.removeAllViews()
|
donationItems?.removeAllViews()
|
||||||
noticeItems?.removeAllViews()
|
noticeItems?.removeAllViews()
|
||||||
scheduleTimeline?.removeAllViews()
|
scheduleTimeline?.removeAllViews()
|
||||||
@@ -351,6 +372,7 @@ class CreatorChannelHomeSectionAdapter(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun bindCommunities(item: CreatorChannelHomeSection.Communities) {
|
private fun bindCommunities(item: CreatorChannelHomeSection.Communities) {
|
||||||
|
communityMoreButton?.setOnClickListener { onSectionChevronClick(CreatorChannelTab.Community) }
|
||||||
val visibleCommunities = item.communities.take(MAX_COMMUNITY_ITEM_COUNT)
|
val visibleCommunities = item.communities.take(MAX_COMMUNITY_ITEM_COUNT)
|
||||||
visibleCommunities.forEachIndexed { index, community ->
|
visibleCommunities.forEachIndexed { index, community ->
|
||||||
val communityWidthDp = calculateCreatorChannelCommunityCardWidthDp(
|
val communityWidthDp = calculateCreatorChannelCommunityCardWidthDp(
|
||||||
@@ -377,6 +399,7 @@ class CreatorChannelHomeSectionAdapter(
|
|||||||
).apply {
|
).apply {
|
||||||
bottomMargin = if (index == visibleCommunities.lastIndex) 0 else 8.dp()
|
bottomMargin = if (index == visibleCommunities.lastIndex) 0 else 8.dp()
|
||||||
}
|
}
|
||||||
|
row.setOnClickListener { onCommunityClick(community.postId) }
|
||||||
communityItems?.addView(row)
|
communityItems?.addView(row)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -590,6 +613,15 @@ class CreatorChannelHomeSectionAdapter(
|
|||||||
is CreatorChannelHomeSection.Sns -> R.string.creator_channel_section_sns
|
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_DONATION_ITEM_COUNT = 8
|
||||||
private const val MAX_NOTICE_ITEM_COUNT = 3
|
private const val MAX_NOTICE_ITEM_COUNT = 3
|
||||||
private const val MAX_SCHEDULE_ITEM_COUNT = 3
|
private const val MAX_SCHEDULE_ITEM_COUNT = 3
|
||||||
|
|||||||
@@ -980,6 +980,68 @@ class CreatorChannelActivitySourceTest {
|
|||||||
assertTrue(activity.contains("dialog.show(screenWidth - 26.7f.dpToPx().toInt())"))
|
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
|
@Test
|
||||||
fun `후원 can 수량은 요청된 배경색 resource 경계로 매핑한다`() {
|
fun `후원 can 수량은 요청된 배경색 resource 경계로 매핑한다`() {
|
||||||
assertEquals(R.color.gray_200, calculateCreatorChannelDonationHeaderColorRes(1))
|
assertEquals(R.color.gray_200, calculateCreatorChannelDonationHeaderColorRes(1))
|
||||||
|
|||||||
Reference in New Issue
Block a user