feat(creator): 라이브 정렬 팝업을 연결한다
This commit is contained in:
@@ -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.toLabelResId
|
||||||
import kr.co.vividnext.sodalive.v2.creator.channel.live.model.toReplayUiModel
|
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.CreatorChannelLiveReplayAdapter
|
||||||
|
import kr.co.vividnext.sodalive.v2.creator.channel.live.ui.CreatorChannelLiveSortPopup
|
||||||
import kr.co.vividnext.sodalive.v2.creator.channel.ui.formatCreatorChannelLiveDateTime
|
import kr.co.vividnext.sodalive.v2.creator.channel.ui.formatCreatorChannelLiveDateTime
|
||||||
import org.koin.androidx.viewmodel.ext.android.viewModel
|
import org.koin.androidx.viewmodel.ext.android.viewModel
|
||||||
|
|
||||||
@@ -25,6 +26,8 @@ class CreatorChannelLiveFragment : BaseFragment<FragmentCreatorChannelLiveBindin
|
|||||||
host.onCreatorChannelLiveReplayClicked(item.audioContentId)
|
host.onCreatorChannelLiveReplayClicked(item.audioContentId)
|
||||||
}
|
}
|
||||||
private var lastContentLayoutKey: CreatorChannelLiveContentLayoutKey? = null
|
private var lastContentLayoutKey: CreatorChannelLiveContentLayoutKey? = null
|
||||||
|
private var sortPopup: CreatorChannelLiveSortPopup? = null
|
||||||
|
private var currentContentState: CreatorChannelLiveUiState.Content? = null
|
||||||
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
|
||||||
get() = requireActivity() as Host
|
get() = requireActivity() as Host
|
||||||
@@ -44,6 +47,9 @@ class CreatorChannelLiveFragment : BaseFragment<FragmentCreatorChannelLiveBindin
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroyView() {
|
override fun onDestroyView() {
|
||||||
|
sortPopup?.dismiss()
|
||||||
|
sortPopup = null
|
||||||
|
currentContentState = null
|
||||||
binding.rvCreatorChannelLiveReplays.adapter = null
|
binding.rvCreatorChannelLiveReplays.adapter = null
|
||||||
super.onDestroyView()
|
super.onDestroyView()
|
||||||
}
|
}
|
||||||
@@ -59,6 +65,9 @@ class CreatorChannelLiveFragment : BaseFragment<FragmentCreatorChannelLiveBindin
|
|||||||
|
|
||||||
private fun setupClickListeners() {
|
private fun setupClickListeners() {
|
||||||
binding.ivCreatorChannelLiveSort.setImageResource(R.drawable.ic_new_sort)
|
binding.ivCreatorChannelLiveSort.setImageResource(R.drawable.ic_new_sort)
|
||||||
|
binding.layoutCreatorChannelLiveSortButton.setOnClickListener {
|
||||||
|
currentContentState?.let { state -> showSortPopup(state) }
|
||||||
|
}
|
||||||
binding.btnCreatorChannelLiveRetry.setOnClickListener {
|
binding.btnCreatorChannelLiveRetry.setOnClickListener {
|
||||||
viewModel.retryLive()
|
viewModel.retryLive()
|
||||||
}
|
}
|
||||||
@@ -76,6 +85,7 @@ class CreatorChannelLiveFragment : BaseFragment<FragmentCreatorChannelLiveBindin
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun bindLoading() = with(binding) {
|
private fun bindLoading() = with(binding) {
|
||||||
|
currentContentState = null
|
||||||
lastContentLayoutKey = null
|
lastContentLayoutKey = null
|
||||||
layoutCreatorChannelLiveSortBar.isVisible = false
|
layoutCreatorChannelLiveSortBar.isVisible = false
|
||||||
layoutCreatorChannelLiveCurrentCard.isVisible = false
|
layoutCreatorChannelLiveCurrentCard.isVisible = false
|
||||||
@@ -86,6 +96,7 @@ class CreatorChannelLiveFragment : BaseFragment<FragmentCreatorChannelLiveBindin
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun bindEmpty() = with(binding) {
|
private fun bindEmpty() = with(binding) {
|
||||||
|
currentContentState = null
|
||||||
lastContentLayoutKey = null
|
lastContentLayoutKey = null
|
||||||
layoutCreatorChannelLiveSortBar.isVisible = false
|
layoutCreatorChannelLiveSortBar.isVisible = false
|
||||||
layoutCreatorChannelLiveCurrentCard.isVisible = false
|
layoutCreatorChannelLiveCurrentCard.isVisible = false
|
||||||
@@ -97,6 +108,7 @@ class CreatorChannelLiveFragment : BaseFragment<FragmentCreatorChannelLiveBindin
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun bindError(state: CreatorChannelLiveUiState.Error) = with(binding) {
|
private fun bindError(state: CreatorChannelLiveUiState.Error) = with(binding) {
|
||||||
|
currentContentState = null
|
||||||
lastContentLayoutKey = null
|
lastContentLayoutKey = null
|
||||||
layoutCreatorChannelLiveSortBar.isVisible = false
|
layoutCreatorChannelLiveSortBar.isVisible = false
|
||||||
layoutCreatorChannelLiveCurrentCard.isVisible = false
|
layoutCreatorChannelLiveCurrentCard.isVisible = false
|
||||||
@@ -109,6 +121,7 @@ class CreatorChannelLiveFragment : BaseFragment<FragmentCreatorChannelLiveBindin
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun bindContent(state: CreatorChannelLiveUiState.Content) = with(binding) {
|
private fun bindContent(state: CreatorChannelLiveUiState.Content) = with(binding) {
|
||||||
|
currentContentState = state
|
||||||
tvCreatorChannelLiveEmptyMessage.isVisible = false
|
tvCreatorChannelLiveEmptyMessage.isVisible = false
|
||||||
tvCreatorChannelLiveErrorMessage.isVisible = false
|
tvCreatorChannelLiveErrorMessage.isVisible = false
|
||||||
btnCreatorChannelLiveRetry.isVisible = false
|
btnCreatorChannelLiveRetry.isVisible = false
|
||||||
@@ -125,6 +138,15 @@ class CreatorChannelLiveFragment : BaseFragment<FragmentCreatorChannelLiveBindin
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun showSortPopup(state: CreatorChannelLiveUiState.Content) {
|
||||||
|
sortPopup?.dismiss()
|
||||||
|
sortPopup = CreatorChannelLiveSortPopup(
|
||||||
|
anchor = binding.layoutCreatorChannelLiveSortButton,
|
||||||
|
selectedSort = state.selectedSort,
|
||||||
|
onSortSelected = { sort -> viewModel.changeSort(sort) }
|
||||||
|
).also { it.show() }
|
||||||
|
}
|
||||||
|
|
||||||
private fun notifyContentChangedIfLayoutChanged(state: CreatorChannelLiveUiState.Content) {
|
private fun notifyContentChangedIfLayoutChanged(state: CreatorChannelLiveUiState.Content) {
|
||||||
val contentLayoutKey = state.toContentLayoutKey()
|
val contentLayoutKey = state.toContentLayoutKey()
|
||||||
if (contentLayoutKey == lastContentLayoutKey) return
|
if (contentLayoutKey == lastContentLayoutKey) return
|
||||||
|
|||||||
@@ -53,10 +53,36 @@ class CreatorChannelLiveFragmentLayoutTest {
|
|||||||
|
|
||||||
assertNotNull(sortBar.findViewById<TextView>(R.id.tv_creator_channel_live_total_label))
|
assertNotNull(sortBar.findViewById<TextView>(R.id.tv_creator_channel_live_total_label))
|
||||||
assertNotNull(sortBar.findViewById<TextView>(R.id.tv_creator_channel_live_total_count))
|
assertNotNull(sortBar.findViewById<TextView>(R.id.tv_creator_channel_live_total_count))
|
||||||
|
assertNotNull(sortBar.findViewById<View>(R.id.layout_creator_channel_live_sort_button))
|
||||||
assertNotNull(sortBar.findViewById<TextView>(R.id.tv_creator_channel_live_sort_label))
|
assertNotNull(sortBar.findViewById<TextView>(R.id.tv_creator_channel_live_sort_label))
|
||||||
assertNotNull(sortBar.findViewById<ImageView>(R.id.iv_creator_channel_live_sort))
|
assertNotNull(sortBar.findViewById<ImageView>(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<View>(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
|
@Test
|
||||||
fun `라이브 다시듣기 item layout은 썸네일 tag title duration action 영역을 제공한다`() {
|
fun `라이브 다시듣기 item layout은 썸네일 tag title duration action 영역을 제공한다`() {
|
||||||
val item = inflateView(R.layout.item_creator_channel_live_replay)
|
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(layout.contains("android:layout_height=\"match_parent\""))
|
||||||
assertTrue(fragment.contains("R.drawable.ic_new_sort"))
|
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("super.onViewCreated(view, savedInstanceState)\n bindLoading()"))
|
||||||
assertTrue(fragment.contains("retryLive()"))
|
assertTrue(fragment.contains("retryLive()"))
|
||||||
assertTrue(fragment.contains("fun onCreatorChannelLiveTabSelected()"))
|
assertTrue(fragment.contains("fun onCreatorChannelLiveTabSelected()"))
|
||||||
@@ -165,6 +196,24 @@ class CreatorChannelLiveFragmentLayoutTest {
|
|||||||
assertTrue(adapter.contains("layoutCreatorChannelLiveReplayActionText.isVisible = true"))
|
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 {
|
private fun inflateView(layoutResId: Int): View {
|
||||||
val context = ApplicationProvider.getApplicationContext<Context>()
|
val context = ApplicationProvider.getApplicationContext<Context>()
|
||||||
return LayoutInflater.from(context).inflate(layoutResId, null, false)
|
return LayoutInflater.from(context).inflate(layoutResId, null, false)
|
||||||
|
|||||||
Reference in New Issue
Block a user