diff --git a/app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelHomeFragment.kt b/app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelHomeFragment.kt new file mode 100644 index 00000000..4c8f218a --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelHomeFragment.kt @@ -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::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) } + } + } + } +} diff --git a/app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelPagerAdapter.kt b/app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelPagerAdapter.kt new file mode 100644 index 00000000..32bcebae --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelPagerAdapter.kt @@ -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.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) + } + } +} diff --git a/app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelPlaceholderFragment.kt b/app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelPlaceholderFragment.kt new file mode 100644 index 00000000..b040a58a --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelPlaceholderFragment.kt @@ -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::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) } + } + } + } +} diff --git a/app/src/main/res/layout/fragment_creator_channel_home.xml b/app/src/main/res/layout/fragment_creator_channel_home.xml new file mode 100644 index 00000000..2537cc37 --- /dev/null +++ b/app/src/main/res/layout/fragment_creator_channel_home.xml @@ -0,0 +1,11 @@ + + diff --git a/app/src/main/res/layout/fragment_creator_channel_placeholder.xml b/app/src/main/res/layout/fragment_creator_channel_placeholder.xml new file mode 100644 index 00000000..83ec3d34 --- /dev/null +++ b/app/src/main/res/layout/fragment_creator_channel_placeholder.xml @@ -0,0 +1,16 @@ + + + + +