feat(creator): 채널 탭 프래그먼트 골격을 추가한다

This commit is contained in:
2026-06-16 14:35:23 +09:00
parent 5ac900f3b7
commit 984aa13edf
5 changed files with 188 additions and 0 deletions

View File

@@ -0,0 +1,109 @@
package kr.co.vividnext.sodalive.v2.creator.channel
import android.os.Bundle
import android.view.View
import android.widget.Toast
import androidx.recyclerview.widget.LinearLayoutManager
import kr.co.vividnext.sodalive.base.BaseFragment
import kr.co.vividnext.sodalive.databinding.FragmentCreatorChannelHomeBinding
import kr.co.vividnext.sodalive.v2.creator.channel.data.CreatorChannelAudioContentResponse
import kr.co.vividnext.sodalive.v2.creator.channel.data.CreatorChannelScheduleResponse
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.ui.CreatorChannelHomeSectionAdapter
import org.koin.androidx.viewmodel.ext.android.viewModel
class CreatorChannelHomeFragment : BaseFragment<FragmentCreatorChannelHomeBinding>(
FragmentCreatorChannelHomeBinding::inflate
) {
private val viewModel: CreatorChannelHomeViewModel by viewModel()
private val sectionAdapter = CreatorChannelHomeSectionAdapter(::onScheduleClicked, ::onAudioContentClicked)
private val creatorId: Long by lazy { arguments?.getLong(ARG_CREATOR_ID) ?: 0L }
private val host: Host
get() = requireActivity() as Host
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.rvHomeSections.layoutManager = LinearLayoutManager(requireContext())
binding.rvHomeSections.adapter = sectionAdapter
observeViewModel()
host.onCreatorChannelHomeActionDelegateReady(
object : HomeActionDelegate {
override fun follow(follow: Boolean, notify: Boolean) {
viewModel.follow(follow = follow, notify = notify)
}
override fun createChatRoom(characterId: Long) {
viewModel.createChatRoom(characterId)
}
}
)
if (creatorId > 0L) {
viewModel.loadHome(creatorId)
}
}
override fun onDestroyView() {
host.onCreatorChannelHomeActionDelegateReady(null)
super.onDestroyView()
}
private fun observeViewModel() {
viewModel.homeStateLiveData.observe(viewLifecycleOwner) { state ->
when (state) {
is CreatorChannelHomeUiState.Content -> {
host.onCreatorChannelHeaderChanged(state.header)
sectionAdapter.submitItems(state.sections)
}
is CreatorChannelHomeUiState.Error -> Unit
CreatorChannelHomeUiState.Empty -> Unit
CreatorChannelHomeUiState.Loading -> Unit
}
}
viewModel.chatRoomIdLiveData.observe(viewLifecycleOwner) { event ->
event.consume()?.let(host::onCreatorChannelChatRoomCreated)
}
viewModel.toastLiveData.observe(viewLifecycleOwner) { event ->
event.consume()?.let {
val message = it.message ?: it.resId?.let(::getString)
message?.let { text -> Toast.makeText(requireContext(), text, Toast.LENGTH_LONG).show() }
}
}
viewModel.isFollowInProgressLiveData.observe(viewLifecycleOwner) {
host.onCreatorChannelFollowProgressChanged(it)
}
}
private fun onScheduleClicked(schedule: CreatorChannelScheduleResponse) {
host.onCreatorChannelScheduleClicked(schedule)
}
private fun onAudioContentClicked(audioContent: CreatorChannelAudioContentResponse) {
host.onCreatorChannelAudioContentClicked(audioContent)
}
interface Host {
fun onCreatorChannelHeaderChanged(header: CreatorChannelHeaderUiModel)
fun onCreatorChannelFollowProgressChanged(inProgress: Boolean)
fun onCreatorChannelChatRoomCreated(chatRoomId: Long)
fun onCreatorChannelScheduleClicked(schedule: CreatorChannelScheduleResponse)
fun onCreatorChannelAudioContentClicked(audioContent: CreatorChannelAudioContentResponse)
fun onCreatorChannelHomeActionDelegateReady(delegate: HomeActionDelegate?)
}
interface HomeActionDelegate {
fun follow(follow: Boolean, notify: Boolean)
fun createChatRoom(characterId: Long)
}
companion object {
private const val ARG_CREATOR_ID: String = "arg_creator_id"
fun newInstance(creatorId: Long): CreatorChannelHomeFragment {
return CreatorChannelHomeFragment().apply {
arguments = Bundle().apply { putLong(ARG_CREATOR_ID, creatorId) }
}
}
}
}

View File

@@ -0,0 +1,23 @@
package kr.co.vividnext.sodalive.v2.creator.channel
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.viewpager2.adapter.FragmentStateAdapter
import kr.co.vividnext.sodalive.v2.creator.channel.model.CreatorChannelTab
class CreatorChannelPagerAdapter(
activity: FragmentActivity,
private val creatorId: Long,
private val tabs: List<CreatorChannelTab> = CreatorChannelTab.entries
) : FragmentStateAdapter(activity) {
override fun getItemCount(): Int = tabs.size
override fun createFragment(position: Int): Fragment {
val tab = tabs[position]
return when (tab) {
CreatorChannelTab.Home -> CreatorChannelHomeFragment.newInstance(creatorId)
else -> CreatorChannelPlaceholderFragment.newInstance(tab)
}
}
}

View File

@@ -0,0 +1,29 @@
package kr.co.vividnext.sodalive.v2.creator.channel
import android.os.Bundle
import android.view.View
import kr.co.vividnext.sodalive.base.BaseFragment
import kr.co.vividnext.sodalive.databinding.FragmentCreatorChannelPlaceholderBinding
import kr.co.vividnext.sodalive.v2.creator.channel.model.CreatorChannelTab
class CreatorChannelPlaceholderFragment : BaseFragment<FragmentCreatorChannelPlaceholderBinding>(
FragmentCreatorChannelPlaceholderBinding::inflate
) {
private val tabName: String by lazy { arguments?.getString(ARG_TAB_NAME).orEmpty() }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.tvPlaceholder.text = tabName
}
companion object {
private const val ARG_TAB_NAME: String = "arg_tab_name"
fun newInstance(tab: CreatorChannelTab): CreatorChannelPlaceholderFragment {
return CreatorChannelPlaceholderFragment().apply {
arguments = Bundle().apply { putString(ARG_TAB_NAME, tab.name) }
}
}
}
}

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.recyclerview.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/rv_home_sections"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipToPadding="false"
android:nestedScrollingEnabled="false"
android:paddingBottom="@dimen/spacing_32"
tools:itemCount="4"
tools:listitem="@layout/item_creator_channel_home_audio" />

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/black">
<TextView
android:id="@+id/tv_placeholder"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:textColor="@color/gray_500"
android:visibility="gone"
tools:text="Live" />
</FrameLayout>