From 8213d2de42abf79477b08adeae12d8de5a087304 Mon Sep 17 00:00:00 2001 From: klaus Date: Thu, 18 Jun 2026 00:11:49 +0900 Subject: [PATCH] =?UTF-8?q?feat(creator):=20=EB=9D=BC=EC=9D=B4=EB=B8=8C=20?= =?UTF-8?q?=EC=A0=95=EB=A0=AC=20=ED=8C=9D=EC=97=85=EC=9D=84=20=EC=97=B0?= =?UTF-8?q?=EA=B2=B0=ED=95=9C=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../live/CreatorChannelLiveFragment.kt | 22 +++++++++ .../CreatorChannelLiveFragmentLayoutTest.kt | 49 +++++++++++++++++++ 2 files changed, 71 insertions(+) diff --git a/app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/live/CreatorChannelLiveFragment.kt b/app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/live/CreatorChannelLiveFragment.kt index 6b6ec3f7..5870727b 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/live/CreatorChannelLiveFragment.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/live/CreatorChannelLiveFragment.kt @@ -13,6 +13,7 @@ import kr.co.vividnext.sodalive.v2.creator.channel.data.CreatorChannelLiveRespon import kr.co.vividnext.sodalive.v2.creator.channel.live.model.toLabelResId import kr.co.vividnext.sodalive.v2.creator.channel.live.model.toReplayUiModel import kr.co.vividnext.sodalive.v2.creator.channel.live.ui.CreatorChannelLiveReplayAdapter +import kr.co.vividnext.sodalive.v2.creator.channel.live.ui.CreatorChannelLiveSortPopup import kr.co.vividnext.sodalive.v2.creator.channel.ui.formatCreatorChannelLiveDateTime import org.koin.androidx.viewmodel.ext.android.viewModel @@ -25,6 +26,8 @@ class CreatorChannelLiveFragment : BaseFragment showSortPopup(state) } + } binding.btnCreatorChannelLiveRetry.setOnClickListener { viewModel.retryLive() } @@ -76,6 +85,7 @@ class CreatorChannelLiveFragment : BaseFragment viewModel.changeSort(sort) } + ).also { it.show() } + } + private fun notifyContentChangedIfLayoutChanged(state: CreatorChannelLiveUiState.Content) { val contentLayoutKey = state.toContentLayoutKey() if (contentLayoutKey == lastContentLayoutKey) return diff --git a/app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/live/CreatorChannelLiveFragmentLayoutTest.kt b/app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/live/CreatorChannelLiveFragmentLayoutTest.kt index 5c95e42d..c5b0a493 100644 --- a/app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/live/CreatorChannelLiveFragmentLayoutTest.kt +++ b/app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/live/CreatorChannelLiveFragmentLayoutTest.kt @@ -53,10 +53,36 @@ class CreatorChannelLiveFragmentLayoutTest { assertNotNull(sortBar.findViewById(R.id.tv_creator_channel_live_total_label)) assertNotNull(sortBar.findViewById(R.id.tv_creator_channel_live_total_count)) + assertNotNull(sortBar.findViewById(R.id.layout_creator_channel_live_sort_button)) assertNotNull(sortBar.findViewById(R.id.tv_creator_channel_live_sort_label)) assertNotNull(sortBar.findViewById(R.id.iv_creator_channel_live_sort)) } + @Test + fun `라이브 sort popup layout은 Figma 기준 option row와 선택 배경을 제공한다`() { + val popup = inflateView(R.layout.view_creator_channel_live_sort_menu) + val popupLayout = projectFile( + "app/src/main/res/layout/view_creator_channel_live_sort_menu.xml" + ).readText() + val popupBackground = projectFile( + "app/src/main/res/drawable/bg_creator_channel_live_sort_popup.xml" + ).readText() + val selectedBackground = projectFile( + "app/src/main/res/drawable/bg_creator_channel_live_sort_selected.xml" + ).readText() + + assertNotNull(popup.findViewById(R.id.layout_creator_channel_live_sort_options)) + assertTrue(popupLayout.contains("@drawable/bg_creator_channel_live_sort_popup")) + assertTrue(popupLayout.contains("@+id/layout_creator_channel_live_sort_options")) + assertTrue(popupLayout.contains("android:paddingHorizontal=\"@dimen/spacing_12\"")) + assertTrue(popupLayout.contains("android:paddingVertical=\"@dimen/spacing_8\"")) + assertTrue(popupLayout.contains("android:fontFamily=\"@font/medium\"")) + assertTrue(popupBackground.contains("android:radius=\"@dimen/radius_14\"")) + assertTrue(popupBackground.contains("android:color=\"@color/gray_900\"")) + assertTrue(popupBackground.contains("android:color=\"@color/gray_700\"")) + assertTrue(selectedBackground.contains("android:color=\"@color/gray_800\"")) + } + @Test fun `라이브 다시듣기 item layout은 썸네일 tag title duration action 영역을 제공한다`() { val item = inflateView(R.layout.item_creator_channel_live_replay) @@ -139,6 +165,11 @@ class CreatorChannelLiveFragmentLayoutTest { assertTrue(layout.contains("android:layout_height=\"match_parent\"")) assertTrue(fragment.contains("R.drawable.ic_new_sort")) + assertTrue(fragment.contains("CreatorChannelLiveSortPopup")) + assertTrue(fragment.contains("layoutCreatorChannelLiveSortButton.setOnClickListener")) + assertTrue(fragment.contains("showSortPopup")) + assertTrue(fragment.contains("viewModel.changeSort(sort)")) + assertTrue(fragment.contains("sortPopup?.dismiss()")) assertTrue(fragment.contains("super.onViewCreated(view, savedInstanceState)\n bindLoading()")) assertTrue(fragment.contains("retryLive()")) assertTrue(fragment.contains("fun onCreatorChannelLiveTabSelected()")) @@ -165,6 +196,24 @@ class CreatorChannelLiveFragmentLayoutTest { assertTrue(adapter.contains("layoutCreatorChannelLiveReplayActionText.isVisible = true")) } + @Test + fun `라이브 sort popup source는 outside dismiss와 화면 밖 보정 및 같은 정렬 dismiss를 포함한다`() { + val popup = projectFile( + "app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/live/ui/CreatorChannelLiveSortPopup.kt" + ).readText() + + assertTrue(popup.contains("PopupWindow")) + assertTrue(popup.contains("isOutsideTouchable = true")) + assertTrue(popup.contains("isFocusable = true")) + assertTrue(popup.contains("toSortOptionUiModel")) + assertTrue(popup.contains("if (option.sort != selectedSort)")) + assertTrue(popup.contains("onSortSelected(option.sort)")) + assertTrue(popup.contains("dismiss()")) + assertTrue(popup.contains("calculateHorizontalOffset")) + assertTrue(popup.contains("visibleDisplayFrame")) + assertTrue(popup.contains("showAsDropDown")) + } + private fun inflateView(layoutResId: Int): View { val context = ApplicationProvider.getApplicationContext() return LayoutInflater.from(context).inflate(layoutResId, null, false)