fix(creator): 라이브 탭 하단 CTA와 sticky 전환을 보정한다

This commit is contained in:
2026-06-18 15:21:18 +09:00
parent a1e8f8edb3
commit f4af9868e6
3 changed files with 183 additions and 43 deletions

View File

@@ -932,16 +932,57 @@ class CreatorChannelActivitySourceTest {
assertTrue(source.contains("override fun onCreatorChannelLiveContentChanged()"))
assertTrue(source.contains("postCheckCreatorChannelLiveNeedsMore()"))
assertTrue(source.contains("updateViewPagerHeight()"))
assertTrue(source.contains("updateCreatorChannelLiveViewportHeight()"))
assertTrue(source.contains("if (position == CreatorChannelTab.Live.ordinal)"))
assertTrue(source.contains("findLiveFragment()?.onCreatorChannelLiveTabSelected()"))
assertTrue(source.contains("binding.viewPager.offscreenPageLimit = CreatorChannelTab.entries.size - 1"))
assertTrue(source.contains("currentPage.minimumHeight = calculateCreatorChannelTabViewportHeight()"))
assertTrue(source.contains("private fun calculateCreatorChannelTabViewportHeight(): Int"))
assertFalse(source.contains("currentPage.minimumHeight = calculateCreatorChannelTabViewportHeight()"))
assertFalse(source.contains("private fun calculateCreatorChannelTabViewportHeight(): Int"))
assertTrue(fragment.contains("fun onCreatorChannelLiveScrolledToBottom()"))
assertTrue(fragment.contains("fun onCreatorChannelLiveTabSelected()"))
assertTrue(fragment.contains("fun onCreatorChannelLiveViewportHeightChanged(minHeight: Int)"))
assertTrue(fragment.contains("fun onCreatorChannelLiveContentChanged()"))
}
@Test
fun `라이브 empty 최소 높이는 sticky anchor 이후 탭 viewport 기준으로 전달한다`() {
val source = projectFile(
"app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt"
).readText()
assertTrue(source.contains("private fun updateCreatorChannelLiveViewportHeight()"))
assertTrue(source.contains("findLiveFragment()?.onCreatorChannelLiveViewportHeightChanged"))
assertTrue(source.contains("calculateCreatorChannelLiveEmptyMinHeight()"))
assertTrue(source.contains("private fun calculateCreatorChannelLiveEmptyMinHeight(): Int"))
assertTrue(source.contains("val stickyScrollY = calculateCreatorChannelStickyScrollY()"))
assertTrue(source.contains("binding.nestedScrollView.height - binding.tabLayout.height"))
assertTrue(source.contains("val scrollRangeRequiredHeight = binding.nestedScrollView.height +"))
assertTrue(source.contains("stickyScrollY -"))
assertTrue(source.contains("binding.headerContainer.height"))
assertTrue(source.contains("return maxOf(visibleTabViewportHeight, scrollRangeRequiredHeight, 0)"))
}
@Test
fun `탭 전환은 sticky tabbar anchor 아래로 내려간 scroll 위치를 되돌리지 않고 부족할 때만 보정한다`() {
val source = projectFile(
"app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt"
).readText()
assertTrue(source.contains("private var lastSelectedCreatorChannelTabPosition: Int? = null"))
assertTrue(source.contains("lastSelectedCreatorChannelTabPosition = binding.viewPager.currentItem"))
assertTrue(source.contains("lastSelectedCreatorChannelTabPosition = position"))
assertTrue(source.contains("adjustCreatorChannelStickyAnchorOnTabSelected(position)"))
assertTrue(source.contains("private fun adjustCreatorChannelStickyAnchorOnTabSelected(position: Int)"))
assertTrue(source.contains("val previousPosition = lastSelectedCreatorChannelTabPosition"))
assertTrue(source.contains("if (previousPosition == null || previousPosition == position)"))
assertTrue(source.contains("val stickyScrollY = calculateCreatorChannelStickyScrollY()"))
assertTrue(source.contains("if (binding.nestedScrollView.scrollY < stickyScrollY)"))
assertTrue(source.contains("binding.nestedScrollView.scrollTo(0, stickyScrollY)"))
assertTrue(source.contains("private fun calculateCreatorChannelStickyScrollY(): Int"))
assertTrue(source.contains("CreatorChannelScrollState.calculateStickyTop(statusBarHeight, baseTitleBarHeight)"))
assertTrue(source.contains("return (binding.headerContainer.height - stickyTop).coerceAtLeast(0)"))
}
@Test
fun `라이브 content 변경은 현재 scroll bottom 조건을 재평가한다`() {
val source = projectFile(
@@ -999,18 +1040,39 @@ class CreatorChannelActivitySourceTest {
val source = projectFile(
"app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt"
).readText()
val fragment = projectFile(
"app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/live/CreatorChannelLiveFragment.kt"
).readText()
val activityLayout = projectFile("app/src/main/res/layout/activity_creator_channel.xml").readText()
assertTrue(source.contains("findLiveFragment()?.onCreatorChannelOwnerChanged(header.isOwner)"))
assertTrue(source.contains("override fun isCreatorChannelOwner(): Boolean"))
assertTrue(activityLayout.contains("android:id=\"@+id/layout_creator_channel_live_owner_cta\""))
assertTrue(source.contains("updateLiveOwnerCtaVisibility()"))
assertTrue(
source.contains(
"currentHeader?.isOwner == true && binding.viewPager.currentItem == CreatorChannelTab.Live.ordinal"
)
)
assertTrue(source.contains("binding.layoutCreatorChannelLiveOwnerCta.isVisible = shouldShowLiveOwnerCta"))
assertTrue(source.contains("findLiveFragment()?.onCreatorChannelLiveOwnerCtaVisibilityChanged(shouldShowLiveOwnerCta)"))
assertTrue(source.contains("return currentHeader?.isOwner == true"))
assertTrue(source.contains("override fun onCreatorChannelLiveStartClicked()"))
assertTrue(source.contains("binding.btnCreatorChannelLiveOwnerCta.setOnClickListener { onLiveOwnerCtaClicked() }"))
assertTrue(source.contains("onOwnerFabLiveClicked()"))
assertTrue(source.contains("liveRoomCreateLauncher.launch(Intent(this, LiveRoomCreateActivity::class.java))"))
assertTrue(fragment.contains("fun onCreatorChannelOwnerChanged(isOwner: Boolean)"))
assertTrue(fragment.contains("host.onCreatorChannelLiveStartClicked()"))
}
@Test
fun `크리에이터 채널 하단 고정 UI는 BaseActivity root bottom padding과 navigation inset을 중복 적용하지 않는다`() {
val source = projectFile(
"app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt"
).readText()
assertFalse(source.contains("binding.ownerFabButton.updateLayoutParams<ConstraintLayout.LayoutParams>"))
assertFalse(source.contains("binding.ownerFabExpandedContainer.updateLayoutParams<ConstraintLayout.LayoutParams>"))
assertFalse(source.contains("binding.layoutCreatorChannelLiveOwnerCta.updateLayoutParams<ConstraintLayout.LayoutParams>"))
assertFalse(source.contains("WindowInsetsCompat.Type.navigationBars()).bottom"))
assertTrue(
source.contains(
"binding.viewPager.updatePadding(bottom = OWNER_FAB_CONTENT_BOTTOM_PADDING_DP.dpToPx().toInt())"
)
)
assertFalse(source.contains("binding.nestedScrollView.updatePadding(bottom = liveOwnerCtaBottomPadding)"))
}
@Test
@@ -1644,17 +1706,16 @@ class CreatorChannelActivitySourceTest {
}
@Test
fun `Phase 13 owner FAB source는 spring animation과 navigation inset을 적용한다`() {
fun `Phase 13 owner FAB source는 spring animation과 content padding을 적용한다`() {
val source = projectFile(
"app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt"
).readText()
assertTrue(source.contains("private var isOwnerFabAnimating: Boolean = false"))
assertTrue(source.contains("setupOwnerFabInsets()"))
assertTrue(source.contains("WindowInsetsCompat.Type.navigationBars()"))
assertTrue(source.contains("OWNER_FAB_BASE_MARGIN_DP.dpToPx().toInt() + navigationBottomInset"))
assertTrue(source.contains("binding.ownerFabButton.updateLayoutParams<ConstraintLayout.LayoutParams>"))
assertTrue(source.contains("binding.ownerFabExpandedContainer.updateLayoutParams<ConstraintLayout.LayoutParams>"))
assertFalse(source.contains("OWNER_FAB_BASE_MARGIN_DP.dpToPx().toInt() + navigationBottomInset"))
assertFalse(source.contains("binding.ownerFabButton.updateLayoutParams<ConstraintLayout.LayoutParams>"))
assertFalse(source.contains("binding.ownerFabExpandedContainer.updateLayoutParams<ConstraintLayout.LayoutParams>"))
assertTrue(
source.contains(
"binding.viewPager.updatePadding(bottom = OWNER_FAB_CONTENT_BOTTOM_PADDING_DP.dpToPx().toInt())"