feat(creator): 후원 floating button 위치를 조정한다

This commit is contained in:
2026-06-22 23:22:26 +09:00
parent 4d79cb65cd
commit 9b19be7775
6 changed files with 79 additions and 24 deletions

View File

@@ -103,6 +103,7 @@ class CreatorChannelActivity :
private var lastSelectedCreatorChannelTabPosition: Int? = null private var lastSelectedCreatorChannelTabPosition: Int? = null
private var isOwnerFabExpanded: Boolean = false private var isOwnerFabExpanded: Boolean = false
private var isOwnerFabAnimating: Boolean = false private var isOwnerFabAnimating: Boolean = false
private var isDonationFloatingButtonVisible: Boolean = false
private lateinit var loadingDialog: LoadingDialog private lateinit var loadingDialog: LoadingDialog
private val baseTitleBarHeight: Int by lazy { 60.dpToPx().toInt() } private val baseTitleBarHeight: Int by lazy { 60.dpToPx().toInt() }
private val liveCoordinator: CreatorChannelLiveCoordinator by lazy { private val liveCoordinator: CreatorChannelLiveCoordinator by lazy {
@@ -189,6 +190,9 @@ class CreatorChannelActivity :
binding.ownerFabCommunityButton.setOnClickListener { onOwnerFabCommunityClicked() } binding.ownerFabCommunityButton.setOnClickListener { onOwnerFabCommunityClicked() }
binding.ownerFabAudioButton.setOnClickListener { onOwnerFabAudioClicked() } binding.ownerFabAudioButton.setOnClickListener { onOwnerFabAudioClicked() }
binding.ownerFabLiveButton.setOnClickListener { onOwnerFabLiveClicked() } binding.ownerFabLiveButton.setOnClickListener { onOwnerFabLiveClicked() }
binding.btnCreatorChannelDonationWrite.setOnClickListener {
findDonationFragment()?.onCreatorChannelDonationFloatingButtonClicked()
}
binding.btnCreatorChannelOwnerCta.setOnClickListener { onOwnerCtaClicked() } binding.btnCreatorChannelOwnerCta.setOnClickListener { onOwnerCtaClicked() }
binding.tvChatButton.setOnClickListener { binding.tvChatButton.setOnClickListener {
currentHeader?.characterId?.let { characterId -> homeActionDelegate?.createChatRoom(characterId) } currentHeader?.characterId?.let { characterId -> homeActionDelegate?.createChatRoom(characterId) }
@@ -419,6 +423,7 @@ class CreatorChannelActivity :
collapseOwnerFab(animate = false) collapseOwnerFab(animate = false)
} }
updateOwnerFabVisibility() updateOwnerFabVisibility()
updateDonationFloatingButtonVisibility()
updateOwnerCtaVisibility() updateOwnerCtaVisibility()
updateCreatorChannelTabViewportHeight() updateCreatorChannelTabViewportHeight()
updateViewPagerHeight() updateViewPagerHeight()
@@ -453,6 +458,7 @@ class CreatorChannelActivity :
bindHeader(header) bindHeader(header)
bindTitleBar(header) bindTitleBar(header)
updateOwnerFabVisibility() updateOwnerFabVisibility()
updateDonationFloatingButtonVisibility()
updateOwnerCtaVisibility() updateOwnerCtaVisibility()
if (binding.viewPager.currentItem == CreatorChannelTab.Audio.ordinal) { if (binding.viewPager.currentItem == CreatorChannelTab.Audio.ordinal) {
binding.viewPager.post { binding.viewPager.post {
@@ -565,6 +571,11 @@ class CreatorChannelActivity :
} }
} }
override fun onCreatorChannelDonationFloatingButtonVisibilityChanged(isVisible: Boolean) {
isDonationFloatingButtonVisible = isVisible
updateDonationFloatingButtonVisibility()
}
override fun onCreatorChannelDonationRequested( override fun onCreatorChannelDonationRequested(
onSubmit: (can: Int, isSecret: Boolean, message: String) -> Unit onSubmit: (can: Int, isSecret: Boolean, message: String) -> Unit
) { ) {
@@ -817,6 +828,14 @@ class CreatorChannelActivity :
} }
} }
private fun updateDonationFloatingButtonVisibility() {
val shouldShowDonationFloatingButton =
isDonationFloatingButtonVisible &&
currentHeader?.isOwner != true &&
binding.viewPager.currentItem == CreatorChannelTab.Donation.ordinal
binding.btnCreatorChannelDonationWrite.isVisible = shouldShowDonationFloatingButton
}
private fun onOwnerFabCommunityClicked() { private fun onOwnerFabCommunityClicked() {
collapseOwnerFab(animate = false) collapseOwnerFab(animate = false)
communityWriteLauncher.launch(Intent(this, CreatorCommunityWriteActivity::class.java)) communityWriteLauncher.launch(Intent(this, CreatorCommunityWriteActivity::class.java))

View File

@@ -34,6 +34,9 @@ class CreatorChannelDonationFragment : BaseFragment<FragmentCreatorChannelDonati
} }
override fun onDestroyView() { override fun onDestroyView() {
if (isAdded) {
host.onCreatorChannelDonationFloatingButtonVisibilityChanged(false)
}
lastContentLayoutKey = null lastContentLayoutKey = null
binding.rvCreatorChannelDonation.adapter = null binding.rvCreatorChannelDonation.adapter = null
super.onDestroyView() super.onDestroyView()
@@ -57,6 +60,12 @@ class CreatorChannelDonationFragment : BaseFragment<FragmentCreatorChannelDonati
binding.root.minimumHeight = minHeight binding.root.minimumHeight = minHeight
} }
fun onCreatorChannelDonationFloatingButtonClicked() {
host.onCreatorChannelDonationRequested { can, isSecret, message ->
viewModel.postChannelDonation(can, isSecret, message)
}
}
private fun setupDonationList() = with(binding.rvCreatorChannelDonation) { private fun setupDonationList() = with(binding.rvCreatorChannelDonation) {
layoutManager = LinearLayoutManager(requireContext()) layoutManager = LinearLayoutManager(requireContext())
adapter = donationAdapter adapter = donationAdapter
@@ -66,11 +75,6 @@ class CreatorChannelDonationFragment : BaseFragment<FragmentCreatorChannelDonati
btnCreatorChannelDonationRetry.setOnClickListener { btnCreatorChannelDonationRetry.setOnClickListener {
viewModel.retryDonations() viewModel.retryDonations()
} }
btnCreatorChannelDonationWrite.setOnClickListener {
host.onCreatorChannelDonationRequested { can, isSecret, message ->
viewModel.postChannelDonation(can, isSecret, message)
}
}
btnCreatorChannelDonationEmptyWrite.setOnClickListener { btnCreatorChannelDonationEmptyWrite.setOnClickListener {
host.onCreatorChannelDonationRequested { can, isSecret, message -> host.onCreatorChannelDonationRequested { can, isSecret, message ->
viewModel.postChannelDonation(can, isSecret, message) viewModel.postChannelDonation(can, isSecret, message)
@@ -99,7 +103,7 @@ class CreatorChannelDonationFragment : BaseFragment<FragmentCreatorChannelDonati
btnCreatorChannelDonationEmptyWrite.isVisible = false btnCreatorChannelDonationEmptyWrite.isVisible = false
tvCreatorChannelDonationErrorMessage.isVisible = false tvCreatorChannelDonationErrorMessage.isVisible = false
btnCreatorChannelDonationRetry.isVisible = false btnCreatorChannelDonationRetry.isVisible = false
btnCreatorChannelDonationWrite.isVisible = false host.onCreatorChannelDonationFloatingButtonVisibilityChanged(false)
} }
private fun bindEmpty(state: CreatorChannelDonationUiState.Empty) = with(binding) { private fun bindEmpty(state: CreatorChannelDonationUiState.Empty) = with(binding) {
@@ -117,7 +121,7 @@ class CreatorChannelDonationFragment : BaseFragment<FragmentCreatorChannelDonati
) )
tvCreatorChannelDonationErrorMessage.isVisible = false tvCreatorChannelDonationErrorMessage.isVisible = false
btnCreatorChannelDonationRetry.isVisible = false btnCreatorChannelDonationRetry.isVisible = false
btnCreatorChannelDonationWrite.isVisible = false host.onCreatorChannelDonationFloatingButtonVisibilityChanged(false)
host.onCreatorChannelDonationContentChanged() host.onCreatorChannelDonationContentChanged()
} }
@@ -130,7 +134,7 @@ class CreatorChannelDonationFragment : BaseFragment<FragmentCreatorChannelDonati
tvCreatorChannelDonationErrorMessage.isVisible = true tvCreatorChannelDonationErrorMessage.isVisible = true
tvCreatorChannelDonationErrorMessage.text = state.message ?: getString(R.string.creator_channel_donation_error_message) tvCreatorChannelDonationErrorMessage.text = state.message ?: getString(R.string.creator_channel_donation_error_message)
btnCreatorChannelDonationRetry.isVisible = true btnCreatorChannelDonationRetry.isVisible = true
btnCreatorChannelDonationWrite.isVisible = false host.onCreatorChannelDonationFloatingButtonVisibilityChanged(false)
host.onCreatorChannelDonationContentChanged() host.onCreatorChannelDonationContentChanged()
} }
@@ -143,7 +147,7 @@ class CreatorChannelDonationFragment : BaseFragment<FragmentCreatorChannelDonati
btnCreatorChannelDonationEmptyWrite.isVisible = false btnCreatorChannelDonationEmptyWrite.isVisible = false
tvCreatorChannelDonationErrorMessage.isVisible = false tvCreatorChannelDonationErrorMessage.isVisible = false
btnCreatorChannelDonationRetry.isVisible = false btnCreatorChannelDonationRetry.isVisible = false
btnCreatorChannelDonationWrite.isVisible = !state.isOwner host.onCreatorChannelDonationFloatingButtonVisibilityChanged(!state.isOwner)
notifyContentChangedIfLayoutChanged(state) notifyContentChangedIfLayoutChanged(state)
state.paginationErrorMessage?.let { state.paginationErrorMessage?.let {
Toast.makeText(requireContext(), it, Toast.LENGTH_SHORT).show() Toast.makeText(requireContext(), it, Toast.LENGTH_SHORT).show()
@@ -179,6 +183,7 @@ class CreatorChannelDonationFragment : BaseFragment<FragmentCreatorChannelDonati
interface Host { interface Host {
fun isCreatorChannelOwner(): Boolean fun isCreatorChannelOwner(): Boolean
fun onCreatorChannelDonationContentChanged() fun onCreatorChannelDonationContentChanged()
fun onCreatorChannelDonationFloatingButtonVisibilityChanged(isVisible: Boolean)
fun onCreatorChannelDonationRequested(onSubmit: (can: Int, isSecret: Boolean, message: String) -> Unit) fun onCreatorChannelDonationRequested(onSubmit: (can: Int, isSecret: Boolean, message: String) -> Unit)
fun onCreatorChannelDonationRankingAllClicked() fun onCreatorChannelDonationRankingAllClicked()
fun onCreatorChannelDonationCompleted() fun onCreatorChannelDonationCompleted()

View File

@@ -382,6 +382,31 @@
</FrameLayout> </FrameLayout>
</LinearLayout> </LinearLayout>
<FrameLayout
android:id="@+id/btn_creator_channel_donation_write"
android:layout_width="66dp"
android:layout_height="66dp"
android:layout_marginEnd="@dimen/spacing_14"
android:layout_marginBottom="@dimen/spacing_14"
android:background="@drawable/bg_creator_channel_owner_fab"
android:clickable="true"
android:contentDescription="@string/creator_channel_donation_action"
android:elevation="8dp"
android:focusable="true"
android:padding="@dimen/spacing_14"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
tools:visibility="visible">
<ImageView
android:layout_width="38dp"
android:layout_height="38dp"
android:contentDescription="@null"
android:src="@drawable/ic_new_donation"
app:tint="@color/white" />
</FrameLayout>
<FrameLayout <FrameLayout
android:id="@+id/owner_fab_button" android:id="@+id/owner_fab_button"
android:layout_width="66dp" android:layout_width="66dp"

View File

@@ -137,16 +137,4 @@
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_creator_channel_donation_error_message" /> app:layout_constraintTop_toBottomOf="@id/tv_creator_channel_donation_error_message" />
<ImageView
android:id="@+id/btn_creator_channel_donation_write"
android:layout_width="58dp"
android:layout_height="58dp"
android:layout_marginEnd="@dimen/spacing_14"
android:layout_marginBottom="@dimen/spacing_14"
android:background="@drawable/bg_creator_channel_fantalk_write_button"
android:contentDescription="@string/creator_channel_donation_action"
android:padding="@dimen/spacing_16"
android:src="@drawable/ic_new_donation"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -428,6 +428,8 @@ class CreatorChannelActivitySourceTest {
val adapter = projectFile( val adapter = projectFile(
"app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelPagerAdapter.kt" "app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelPagerAdapter.kt"
).readText() ).readText()
val activityLayout = projectFile("app/src/main/res/layout/activity_creator_channel.xml").readText()
val donationFragmentLayout = projectFile("app/src/main/res/layout/fragment_creator_channel_donation.xml").readText()
assertTrue(adapter.contains("CreatorChannelDonationFragment.newInstance(creatorId)")) assertTrue(adapter.contains("CreatorChannelDonationFragment.newInstance(creatorId)"))
assertTrue(source.contains("CreatorChannelDonationFragment.Host")) assertTrue(source.contains("CreatorChannelDonationFragment.Host"))
@@ -444,6 +446,20 @@ class CreatorChannelActivitySourceTest {
assertTrue(source.contains("homeActionDelegate?.refreshHome()")) assertTrue(source.contains("homeActionDelegate?.refreshHome()"))
assertTrue(source.contains("UserProfileDonationAllViewActivity::class.java")) assertTrue(source.contains("UserProfileDonationAllViewActivity::class.java"))
assertTrue(source.contains("putExtra(Constants.EXTRA_USER_ID, creatorId)")) assertTrue(source.contains("putExtra(Constants.EXTRA_USER_ID, creatorId)"))
assertTrue(activityLayout.contains("android:id=\"@+id/btn_creator_channel_donation_write\""))
assertTrue(activityLayout.contains("android:layout_width=\"66dp\""))
assertTrue(activityLayout.contains("android:layout_height=\"66dp\""))
assertTrue(activityLayout.contains("android:layout_marginEnd=\"@dimen/spacing_14\""))
assertTrue(activityLayout.contains("android:layout_marginBottom=\"@dimen/spacing_14\""))
assertTrue(activityLayout.contains("android:padding=\"@dimen/spacing_14\""))
assertTrue(activityLayout.contains("app:layout_constraintBottom_toBottomOf=\"parent\""))
assertTrue(activityLayout.contains("app:layout_constraintEnd_toEndOf=\"parent\""))
assertTrue(activityLayout.contains("android:layout_width=\"38dp\""))
assertTrue(source.contains("private var isDonationFloatingButtonVisible: Boolean = false"))
assertTrue(source.contains("updateDonationFloatingButtonVisibility()"))
assertTrue(source.contains("findDonationFragment()?.onCreatorChannelDonationFloatingButtonClicked()"))
assertTrue(donationFragmentLayout.contains("btn_creator_channel_donation_empty_write"))
assertFalse(donationFragmentLayout.contains("btn_creator_channel_donation_write"))
} }
@Test @Test

View File

@@ -33,12 +33,14 @@ class CreatorChannelDonationActionTest {
} }
@Test @Test
fun `후원 fragment source는 owner일 때 floating button을 숨기고 후원 요청을 ViewModel에 전달한다`() { fun `후원 fragment source는 content owner 상태에 따라 Activity floating button 표시를 요청한다`() {
val fragment = projectFile( val fragment = projectFile(
"app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/donation/CreatorChannelDonationFragment.kt" "app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/donation/CreatorChannelDonationFragment.kt"
).readText() ).readText()
assertTrue(fragment.contains("btnCreatorChannelDonationWrite.isVisible = !state.isOwner")) assertTrue(fragment.contains("fun onCreatorChannelDonationFloatingButtonClicked()"))
assertTrue(fragment.contains("host.onCreatorChannelDonationFloatingButtonVisibilityChanged(!state.isOwner)"))
assertTrue(fragment.contains("host.onCreatorChannelDonationFloatingButtonVisibilityChanged(false)"))
assertTrue(fragment.contains("host.onCreatorChannelDonationRequested { can, isSecret, message ->")) assertTrue(fragment.contains("host.onCreatorChannelDonationRequested { can, isSecret, message ->"))
assertTrue(fragment.contains("viewModel.postChannelDonation(can, isSecret, message)")) assertTrue(fragment.contains("viewModel.postChannelDonation(can, isSecret, message)"))
assertTrue(fragment.contains("viewModel.consumeDonationSuccessEvent()")) assertTrue(fragment.contains("viewModel.consumeDonationSuccessEvent()"))
@@ -53,7 +55,7 @@ class CreatorChannelDonationActionTest {
assertTrue(fragment.contains("btnCreatorChannelDonationEmptyWrite.setOnClickListener")) assertTrue(fragment.contains("btnCreatorChannelDonationEmptyWrite.setOnClickListener"))
assertTrue(fragment.contains("btnCreatorChannelDonationEmptyWrite.isVisible = !state.isOwner")) assertTrue(fragment.contains("btnCreatorChannelDonationEmptyWrite.isVisible = !state.isOwner"))
assertTrue(fragment.contains("btnCreatorChannelDonationWrite.isVisible = false")) assertTrue(fragment.contains("host.onCreatorChannelDonationFloatingButtonVisibilityChanged(false)"))
assertTrue(fragment.contains("host.onCreatorChannelDonationRequested { can, isSecret, message ->")) assertTrue(fragment.contains("host.onCreatorChannelDonationRequested { can, isSecret, message ->"))
assertTrue(fragment.contains("viewModel.postChannelDonation(can, isSecret, message)")) assertTrue(fragment.contains("viewModel.postChannelDonation(can, isSecret, message)"))
} }