feat(home): 팔로잉 탭 화면을 연결한다
This commit is contained in:
@@ -14,7 +14,20 @@ import kr.co.vividnext.sodalive.common.formatUtcRelativeTimeText
|
|||||||
import kr.co.vividnext.sodalive.databinding.FragmentV2MainHomeBinding
|
import kr.co.vividnext.sodalive.databinding.FragmentV2MainHomeBinding
|
||||||
import kr.co.vividnext.sodalive.databinding.ViewSectionTitleBinding
|
import kr.co.vividnext.sodalive.databinding.ViewSectionTitleBinding
|
||||||
import kr.co.vividnext.sodalive.explorer.profile.creator_community.all.CreatorCommunityAllActivity
|
import kr.co.vividnext.sodalive.explorer.profile.creator_community.all.CreatorCommunityAllActivity
|
||||||
|
import kr.co.vividnext.sodalive.chat.talk.room.ChatRoomActivity
|
||||||
import kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivity
|
import kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivity
|
||||||
|
import kr.co.vividnext.sodalive.v2.main.chat.dm.DmChatRoomActivity
|
||||||
|
import kr.co.vividnext.sodalive.v2.main.chat.model.ChatRoomListUiItem
|
||||||
|
import kr.co.vividnext.sodalive.v2.main.chat.model.ChatRoomType
|
||||||
|
import kr.co.vividnext.sodalive.v2.main.home.model.HomeFollowingChatSection
|
||||||
|
import kr.co.vividnext.sodalive.v2.main.home.model.HomeFollowingCreatorSection
|
||||||
|
import kr.co.vividnext.sodalive.v2.main.home.model.HomeFollowingLiveSection
|
||||||
|
import kr.co.vividnext.sodalive.v2.main.home.model.HomeFollowingLiveUiItem
|
||||||
|
import kr.co.vividnext.sodalive.v2.main.home.model.HomeFollowingNewsSection
|
||||||
|
import kr.co.vividnext.sodalive.v2.main.home.model.HomeFollowingNewsUiItem
|
||||||
|
import kr.co.vividnext.sodalive.v2.main.home.model.HomeFollowingScheduleSection
|
||||||
|
import kr.co.vividnext.sodalive.v2.main.home.model.HomeFollowingScheduleUiItem
|
||||||
|
import kr.co.vividnext.sodalive.v2.main.home.model.HomeFollowingUiState
|
||||||
import kr.co.vividnext.sodalive.v2.main.home.model.HomeCreatorRankingUiState
|
import kr.co.vividnext.sodalive.v2.main.home.model.HomeCreatorRankingUiState
|
||||||
import kr.co.vividnext.sodalive.v2.main.home.model.HomeRecommendationAiCharacterSection
|
import kr.co.vividnext.sodalive.v2.main.home.model.HomeRecommendationAiCharacterSection
|
||||||
import kr.co.vividnext.sodalive.v2.main.home.model.HomeRecommendationAiCharacterUiModel
|
import kr.co.vividnext.sodalive.v2.main.home.model.HomeRecommendationAiCharacterUiModel
|
||||||
@@ -45,6 +58,11 @@ import kr.co.vividnext.sodalive.v2.main.home.ui.HomeBannerBinder
|
|||||||
import kr.co.vividnext.sodalive.v2.main.home.ui.HomeBusinessInfoBinder
|
import kr.co.vividnext.sodalive.v2.main.home.ui.HomeBusinessInfoBinder
|
||||||
import kr.co.vividnext.sodalive.v2.main.home.ui.HomeCheerCreatorAdapter
|
import kr.co.vividnext.sodalive.v2.main.home.ui.HomeCheerCreatorAdapter
|
||||||
import kr.co.vividnext.sodalive.v2.main.home.ui.HomeFirstAudioAdapter
|
import kr.co.vividnext.sodalive.v2.main.home.ui.HomeFirstAudioAdapter
|
||||||
|
import kr.co.vividnext.sodalive.v2.main.home.ui.HomeFollowingChatAdapter
|
||||||
|
import kr.co.vividnext.sodalive.v2.main.home.ui.HomeFollowingCreatorAdapter
|
||||||
|
import kr.co.vividnext.sodalive.v2.main.home.ui.HomeFollowingLiveAdapter
|
||||||
|
import kr.co.vividnext.sodalive.v2.main.home.ui.HomeFollowingNewsAdapter
|
||||||
|
import kr.co.vividnext.sodalive.v2.main.home.ui.HomeFollowingScheduleAdapter
|
||||||
import kr.co.vividnext.sodalive.v2.main.home.ui.HomeGenreCreatorAdapter
|
import kr.co.vividnext.sodalive.v2.main.home.ui.HomeGenreCreatorAdapter
|
||||||
import kr.co.vividnext.sodalive.v2.main.home.ui.HomeLiveAdapter
|
import kr.co.vividnext.sodalive.v2.main.home.ui.HomeLiveAdapter
|
||||||
import kr.co.vividnext.sodalive.v2.main.home.ui.HomePopularCommunityAdapter
|
import kr.co.vividnext.sodalive.v2.main.home.ui.HomePopularCommunityAdapter
|
||||||
@@ -60,6 +78,7 @@ class HomeMainFragment : BaseFragment<FragmentV2MainHomeBinding>(
|
|||||||
) {
|
) {
|
||||||
private val homeRecommendationViewModel: HomeRecommendationViewModel by viewModel()
|
private val homeRecommendationViewModel: HomeRecommendationViewModel by viewModel()
|
||||||
private val homeCreatorRankingViewModel: HomeCreatorRankingViewModel by viewModel()
|
private val homeCreatorRankingViewModel: HomeCreatorRankingViewModel by viewModel()
|
||||||
|
private val homeFollowingViewModel: HomeFollowingViewModel by viewModel()
|
||||||
private val loadingDialog: LoadingDialog by lazy { LoadingDialog(requireActivity(), layoutInflater) }
|
private val loadingDialog: LoadingDialog by lazy { LoadingDialog(requireActivity(), layoutInflater) }
|
||||||
private val liveAdapter = HomeLiveAdapter()
|
private val liveAdapter = HomeLiveAdapter()
|
||||||
private val recentActivityCreatorAdapter = HomeRecentActivityCreatorAdapter { onRecentActivityClick(it) }
|
private val recentActivityCreatorAdapter = HomeRecentActivityCreatorAdapter { onRecentActivityClick(it) }
|
||||||
@@ -76,10 +95,16 @@ class HomeMainFragment : BaseFragment<FragmentV2MainHomeBinding>(
|
|||||||
)
|
)
|
||||||
private val popularCommunityAdapter = HomePopularCommunityAdapter { openPopularCommunityPost(it) }
|
private val popularCommunityAdapter = HomePopularCommunityAdapter { openPopularCommunityPost(it) }
|
||||||
private val creatorRankingAdapter = CreatorRankingAdapter { openCreatorRankingProfile(it) }
|
private val creatorRankingAdapter = CreatorRankingAdapter { openCreatorRankingProfile(it) }
|
||||||
|
private val followingCreatorAdapter = HomeFollowingCreatorAdapter { openCreatorProfile(it.creatorId) }
|
||||||
|
private val followingLiveAdapter = HomeFollowingLiveAdapter { onFollowingLiveClick(it) }
|
||||||
|
private val followingChatAdapter = HomeFollowingChatAdapter { openFollowingChat(it) }
|
||||||
|
private val followingScheduleAdapter = HomeFollowingScheduleAdapter { onFollowingScheduleClick(it) }
|
||||||
|
private val followingNewsAdapter = HomeFollowingNewsAdapter { onFollowingNewsClick(it) }
|
||||||
private var bannerBinder: HomeBannerBinder? = null
|
private var bannerBinder: HomeBannerBinder? = null
|
||||||
private var onGenreFollowAllClick: (List<Long>) -> Unit = {}
|
private var onGenreFollowAllClick: (List<Long>) -> Unit = {}
|
||||||
private var onCheerFollowAllClick: (List<Long>) -> Unit = {}
|
private var onCheerFollowAllClick: (List<Long>) -> Unit = {}
|
||||||
private var hasLoadedCreatorRankings = false
|
private var hasLoadedCreatorRankings = false
|
||||||
|
private var hasLoadedFollowing = false
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
@@ -97,9 +122,11 @@ class HomeMainFragment : BaseFragment<FragmentV2MainHomeBinding>(
|
|||||||
setUpSectionTitles()
|
setUpSectionTitles()
|
||||||
setUpRecommendationAdapters()
|
setUpRecommendationAdapters()
|
||||||
setUpCreatorRankingAdapter()
|
setUpCreatorRankingAdapter()
|
||||||
|
setUpFollowingAdapters()
|
||||||
setUpBusinessInfo()
|
setUpBusinessInfo()
|
||||||
bindHomeRecommendationObservers()
|
bindHomeRecommendationObservers()
|
||||||
bindHomeCreatorRankingObservers()
|
bindHomeCreatorRankingObservers()
|
||||||
|
bindHomeFollowingObservers()
|
||||||
homeRecommendationViewModel.loadRecommendations()
|
homeRecommendationViewModel.loadRecommendations()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,6 +186,29 @@ class HomeMainFragment : BaseFragment<FragmentV2MainHomeBinding>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun setUpFollowingAdapters() {
|
||||||
|
binding.rvHomeFollowingCreators.apply {
|
||||||
|
layoutManager = LinearLayoutManager(requireContext(), LinearLayoutManager.HORIZONTAL, false)
|
||||||
|
adapter = followingCreatorAdapter
|
||||||
|
}
|
||||||
|
binding.rvHomeFollowingOnAirLives.apply {
|
||||||
|
layoutManager = LinearLayoutManager(requireContext(), LinearLayoutManager.HORIZONTAL, false)
|
||||||
|
adapter = followingLiveAdapter
|
||||||
|
}
|
||||||
|
binding.rvHomeFollowingRecentChats.apply {
|
||||||
|
layoutManager = LinearLayoutManager(requireContext(), LinearLayoutManager.VERTICAL, false)
|
||||||
|
adapter = followingChatAdapter
|
||||||
|
}
|
||||||
|
binding.rvHomeFollowingMonthlySchedules.apply {
|
||||||
|
layoutManager = LinearLayoutManager(requireContext(), LinearLayoutManager.VERTICAL, false)
|
||||||
|
adapter = followingScheduleAdapter
|
||||||
|
}
|
||||||
|
binding.rvHomeFollowingRecentNews.apply {
|
||||||
|
layoutManager = LinearLayoutManager(requireContext(), LinearLayoutManager.VERTICAL, false)
|
||||||
|
adapter = followingNewsAdapter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun bindHomeCreatorRankingObservers() {
|
private fun bindHomeCreatorRankingObservers() {
|
||||||
homeCreatorRankingViewModel.rankingStateLiveData.observe(viewLifecycleOwner) { state ->
|
homeCreatorRankingViewModel.rankingStateLiveData.observe(viewLifecycleOwner) { state ->
|
||||||
when (state) {
|
when (state) {
|
||||||
@@ -186,18 +236,51 @@ class HomeMainFragment : BaseFragment<FragmentV2MainHomeBinding>(
|
|||||||
HOME_TAB_RECOMMENDATION -> {
|
HOME_TAB_RECOMMENDATION -> {
|
||||||
binding.nsvHomeRecommendationContent.visibility = View.VISIBLE
|
binding.nsvHomeRecommendationContent.visibility = View.VISIBLE
|
||||||
binding.rvHomeCreatorRankings.visibility = View.GONE
|
binding.rvHomeCreatorRankings.visibility = View.GONE
|
||||||
|
binding.nsvHomeFollowingContent.visibility = View.GONE
|
||||||
}
|
}
|
||||||
|
|
||||||
HOME_TAB_RANKING -> {
|
HOME_TAB_RANKING -> {
|
||||||
binding.nsvHomeRecommendationContent.visibility = View.GONE
|
binding.nsvHomeRecommendationContent.visibility = View.GONE
|
||||||
binding.rvHomeCreatorRankings.visibility = View.VISIBLE
|
binding.rvHomeCreatorRankings.visibility = View.VISIBLE
|
||||||
|
binding.nsvHomeFollowingContent.visibility = View.GONE
|
||||||
if (!hasLoadedCreatorRankings) {
|
if (!hasLoadedCreatorRankings) {
|
||||||
hasLoadedCreatorRankings = true
|
hasLoadedCreatorRankings = true
|
||||||
homeCreatorRankingViewModel.loadCreatorRankings()
|
homeCreatorRankingViewModel.loadCreatorRankings()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
HOME_TAB_FOLLOWING -> Unit
|
HOME_TAB_FOLLOWING -> {
|
||||||
|
binding.nsvHomeFollowingContent.visibility = View.VISIBLE
|
||||||
|
binding.nsvHomeRecommendationContent.visibility = View.GONE
|
||||||
|
binding.rvHomeCreatorRankings.visibility = View.GONE
|
||||||
|
if (!hasLoadedFollowing) {
|
||||||
|
hasLoadedFollowing = true
|
||||||
|
homeFollowingViewModel.loadFollowing()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun bindHomeFollowingObservers() {
|
||||||
|
homeFollowingViewModel.followingStateLiveData.observe(viewLifecycleOwner) { state ->
|
||||||
|
when (state) {
|
||||||
|
is HomeFollowingUiState.Content -> bindHomeFollowingContent(state)
|
||||||
|
HomeFollowingUiState.LoginRequired,
|
||||||
|
HomeFollowingUiState.Empty,
|
||||||
|
is HomeFollowingUiState.Error -> bindHomeFollowingEmpty()
|
||||||
|
|
||||||
|
HomeFollowingUiState.Loading -> Unit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
homeFollowingViewModel.isLoading.observe(viewLifecycleOwner) { isLoading ->
|
||||||
|
if (isLoading) {
|
||||||
|
loadingDialog.show(screenWidth)
|
||||||
|
} else {
|
||||||
|
loadingDialog.dismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
homeFollowingViewModel.toastLiveData.observe(viewLifecycleOwner) { toastMessage ->
|
||||||
|
toastMessage?.let(::showToast)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -235,6 +318,52 @@ class HomeMainFragment : BaseFragment<FragmentV2MainHomeBinding>(
|
|||||||
bindPopularCommunitySection(content.popularCommunityPosts)
|
bindPopularCommunitySection(content.popularCommunityPosts)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun bindHomeFollowingContent(content: HomeFollowingUiState.Content) {
|
||||||
|
bindFollowingCreatorSection(content.followingCreators)
|
||||||
|
bindFollowingLiveSection(content.onAirLives)
|
||||||
|
bindFollowingChatSection(content.recentChats)
|
||||||
|
bindFollowingScheduleSection(content.monthlySchedules)
|
||||||
|
bindFollowingNewsSection(content.recentNews)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun bindHomeFollowingEmpty() {
|
||||||
|
binding.llHomeFollowingCreatorsSection.visibility = View.GONE
|
||||||
|
binding.llHomeFollowingOnAirSection.visibility = View.GONE
|
||||||
|
binding.llHomeFollowingRecentChatsSection.visibility = View.GONE
|
||||||
|
binding.llHomeFollowingMonthlySchedulesSection.visibility = View.GONE
|
||||||
|
binding.llHomeFollowingRecentNewsSection.visibility = View.GONE
|
||||||
|
followingCreatorAdapter.submitItems(emptyList())
|
||||||
|
followingLiveAdapter.submitItems(emptyList())
|
||||||
|
followingChatAdapter.submitItems(emptyList())
|
||||||
|
followingScheduleAdapter.submitItems(emptyList())
|
||||||
|
followingNewsAdapter.submitItems(emptyList())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun bindFollowingCreatorSection(section: HomeFollowingCreatorSection) {
|
||||||
|
binding.llHomeFollowingCreatorsSection.visibility = section.items.toSectionVisibility()
|
||||||
|
followingCreatorAdapter.submitItems(section.items)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun bindFollowingLiveSection(section: HomeFollowingLiveSection) {
|
||||||
|
binding.llHomeFollowingOnAirSection.visibility = section.items.toSectionVisibility()
|
||||||
|
followingLiveAdapter.submitItems(section.items)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun bindFollowingChatSection(section: HomeFollowingChatSection) {
|
||||||
|
binding.llHomeFollowingRecentChatsSection.visibility = section.items.toSectionVisibility()
|
||||||
|
followingChatAdapter.submitItems(section.items)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun bindFollowingScheduleSection(section: HomeFollowingScheduleSection) {
|
||||||
|
binding.llHomeFollowingMonthlySchedulesSection.visibility = section.items.toSectionVisibility()
|
||||||
|
followingScheduleAdapter.submitItems(section.items)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun bindFollowingNewsSection(section: HomeFollowingNewsSection) {
|
||||||
|
binding.llHomeFollowingRecentNewsSection.visibility = section.items.toSectionVisibility()
|
||||||
|
followingNewsAdapter.submitItems(section.items)
|
||||||
|
}
|
||||||
|
|
||||||
private fun bindLiveSection(section: HomeRecommendationLiveSection) {
|
private fun bindLiveSection(section: HomeRecommendationLiveSection) {
|
||||||
binding.llHomeLiveSection.visibility = section.items.toSectionVisibility()
|
binding.llHomeLiveSection.visibility = section.items.toSectionVisibility()
|
||||||
liveAdapter.submitItems(section.items)
|
liveAdapter.submitItems(section.items)
|
||||||
@@ -319,6 +448,41 @@ class HomeMainFragment : BaseFragment<FragmentV2MainHomeBinding>(
|
|||||||
binding.viewHomePopularCommunityTitle.setTitle(
|
binding.viewHomePopularCommunityTitle.setTitle(
|
||||||
R.string.home_recommendation_section_popular_community_posts
|
R.string.home_recommendation_section_popular_community_posts
|
||||||
)
|
)
|
||||||
|
binding.viewHomeFollowingCreatorsTitle.setTitle(
|
||||||
|
R.string.screen_home_following_creators_title,
|
||||||
|
showMore = true
|
||||||
|
)
|
||||||
|
binding.viewHomeFollowingOnAirTitle.setTitle(
|
||||||
|
R.string.screen_home_following_on_air,
|
||||||
|
showMore = true
|
||||||
|
)
|
||||||
|
binding.viewHomeFollowingRecentChatsTitle.setTitle(
|
||||||
|
R.string.screen_home_following_recent_chats_title,
|
||||||
|
showMore = true
|
||||||
|
)
|
||||||
|
binding.viewHomeFollowingMonthlySchedulesTitle.setTitle(
|
||||||
|
R.string.screen_home_following_monthly_schedules_title,
|
||||||
|
showMore = true
|
||||||
|
)
|
||||||
|
binding.viewHomeFollowingRecentNewsTitle.setTitle(
|
||||||
|
R.string.screen_home_following_recent_news_title,
|
||||||
|
showMore = true
|
||||||
|
)
|
||||||
|
binding.viewHomeFollowingCreatorsTitle.ivSectionTitleChevron.setOnClickListener {
|
||||||
|
onFollowingSectionMoreClick(HomeFollowingSection.CREATORS)
|
||||||
|
}
|
||||||
|
binding.viewHomeFollowingOnAirTitle.ivSectionTitleChevron.setOnClickListener {
|
||||||
|
onFollowingSectionMoreClick(HomeFollowingSection.ON_AIR)
|
||||||
|
}
|
||||||
|
binding.viewHomeFollowingRecentChatsTitle.ivSectionTitleChevron.setOnClickListener {
|
||||||
|
onFollowingSectionMoreClick(HomeFollowingSection.RECENT_CHATS)
|
||||||
|
}
|
||||||
|
binding.viewHomeFollowingMonthlySchedulesTitle.ivSectionTitleChevron.setOnClickListener {
|
||||||
|
onFollowingSectionMoreClick(HomeFollowingSection.MONTHLY_SCHEDULES)
|
||||||
|
}
|
||||||
|
binding.viewHomeFollowingRecentNewsTitle.ivSectionTitleChevron.setOnClickListener {
|
||||||
|
onFollowingSectionMoreClick(HomeFollowingSection.RECENT_NEWS)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun ViewSectionTitleBinding.setTitle(
|
private fun ViewSectionTitleBinding.setTitle(
|
||||||
@@ -331,6 +495,21 @@ class HomeMainFragment : BaseFragment<FragmentV2MainHomeBinding>(
|
|||||||
|
|
||||||
private fun onLiveClick(item: HomeRecommendationLiveUiModel) = Unit
|
private fun onLiveClick(item: HomeRecommendationLiveUiModel) = Unit
|
||||||
|
|
||||||
|
private fun onFollowingSectionMoreClick(section: HomeFollowingSection) = Unit
|
||||||
|
|
||||||
|
private fun onFollowingLiveClick(item: HomeFollowingLiveUiItem) = Unit
|
||||||
|
|
||||||
|
private fun onFollowingScheduleClick(item: HomeFollowingScheduleUiItem) = Unit
|
||||||
|
|
||||||
|
private fun onFollowingNewsClick(item: HomeFollowingNewsUiItem) = Unit
|
||||||
|
|
||||||
|
private fun openFollowingChat(item: ChatRoomListUiItem) {
|
||||||
|
when (item.chatType) {
|
||||||
|
ChatRoomType.AI -> startActivity(ChatRoomActivity.newIntent(requireContext(), item.roomId))
|
||||||
|
ChatRoomType.DM -> startActivity(DmChatRoomActivity.newIntentByRoomId(requireContext(), item.roomId))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun onBannerClick(item: HomeRecommendationBannerUiModel) {
|
private fun onBannerClick(item: HomeRecommendationBannerUiModel) {
|
||||||
val route = item.toHomeRecommendationBannerRoute() ?: return
|
val route = item.toHomeRecommendationBannerRoute() ?: return
|
||||||
startActivity(route.toHomeRecommendationBannerIntent(requireContext()))
|
startActivity(route.toHomeRecommendationBannerIntent(requireContext()))
|
||||||
@@ -419,3 +598,11 @@ class HomeMainFragment : BaseFragment<FragmentV2MainHomeBinding>(
|
|||||||
const val SECTION_KEY_GENRE_CREATORS = "genreCreators"
|
const val SECTION_KEY_GENRE_CREATORS = "genreCreators"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum class HomeFollowingSection {
|
||||||
|
CREATORS,
|
||||||
|
ON_AIR,
|
||||||
|
RECENT_CHATS,
|
||||||
|
MONTHLY_SCHEDULES,
|
||||||
|
RECENT_NEWS
|
||||||
|
}
|
||||||
|
|||||||
@@ -239,6 +239,141 @@
|
|||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</androidx.core.widget.NestedScrollView>
|
</androidx.core.widget.NestedScrollView>
|
||||||
|
|
||||||
|
<androidx.core.widget.NestedScrollView
|
||||||
|
android:id="@+id/nsv_home_following_content"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:fillViewport="true"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/text_tab_bar_home">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/ll_home_following_content"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingBottom="@dimen/spacing_28">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/ll_home_following_creators_section"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="@dimen/spacing_12"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<include
|
||||||
|
android:id="@+id/view_home_following_creators_title"
|
||||||
|
layout="@layout/view_section_title" />
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/rv_home_following_creators"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="@dimen/spacing_14"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:paddingHorizontal="@dimen/spacing_14"
|
||||||
|
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||||
|
tools:listitem="@layout/item_home_following_creator" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/ll_home_following_on_air_section"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="@dimen/spacing_48"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<include
|
||||||
|
android:id="@+id/view_home_following_on_air_title"
|
||||||
|
layout="@layout/view_section_title" />
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/rv_home_following_on_air_lives"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="@dimen/spacing_14"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:paddingHorizontal="@dimen/spacing_14"
|
||||||
|
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||||
|
tools:listitem="@layout/item_home_following_live" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/ll_home_following_recent_chats_section"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="@dimen/spacing_48"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<include
|
||||||
|
android:id="@+id/view_home_following_recent_chats_title"
|
||||||
|
layout="@layout/view_section_title" />
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/rv_home_following_recent_chats"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="@dimen/spacing_14"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingHorizontal="@dimen/spacing_14"
|
||||||
|
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||||
|
tools:listitem="@layout/item_home_following_chat" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/ll_home_following_monthly_schedules_section"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="@dimen/spacing_48"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<include
|
||||||
|
android:id="@+id/view_home_following_monthly_schedules_title"
|
||||||
|
layout="@layout/view_section_title" />
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/rv_home_following_monthly_schedules"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="@dimen/spacing_14"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingHorizontal="@dimen/spacing_14"
|
||||||
|
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||||
|
tools:listitem="@layout/item_home_following_schedule" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/ll_home_following_recent_news_section"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="@dimen/spacing_48"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<include
|
||||||
|
android:id="@+id/view_home_following_recent_news_title"
|
||||||
|
layout="@layout/view_section_title" />
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/rv_home_following_recent_news"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="@dimen/spacing_14"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingHorizontal="@dimen/spacing_14"
|
||||||
|
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||||
|
tools:listitem="@layout/item_home_following_news_content" />
|
||||||
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
</androidx.core.widget.NestedScrollView>
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
android:id="@+id/rv_home_creator_rankings"
|
android:id="@+id/rv_home_creator_rankings"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
|
|||||||
@@ -0,0 +1,144 @@
|
|||||||
|
package kr.co.vividnext.sodalive.v2.main.home
|
||||||
|
|
||||||
|
import org.junit.Assert.assertFalse
|
||||||
|
import org.junit.Assert.assertTrue
|
||||||
|
import org.junit.Test
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
class HomeFollowingFragmentSourceTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `home layout exposes following content and section recycler ids`() {
|
||||||
|
val layout = homeMainLayoutSource()
|
||||||
|
|
||||||
|
assertTrue(layout.contains("@+id/nsv_home_following_content"))
|
||||||
|
assertTrue(layout.contains("@+id/rv_home_following_creators"))
|
||||||
|
assertTrue(layout.contains("@+id/rv_home_following_on_air_lives"))
|
||||||
|
assertTrue(layout.contains("@+id/rv_home_following_recent_chats"))
|
||||||
|
assertTrue(layout.contains("@+id/rv_home_following_monthly_schedules"))
|
||||||
|
assertTrue(layout.contains("@+id/rv_home_following_recent_news"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `home main fragment injects following viewmodel and wires adapters`() {
|
||||||
|
val source = homeMainFragmentSource()
|
||||||
|
|
||||||
|
assertTrue(source.contains("HomeFollowingViewModel"))
|
||||||
|
assertTrue(source.contains("private val homeFollowingViewModel: HomeFollowingViewModel by viewModel()"))
|
||||||
|
assertTrue(source.contains("import kr.co.vividnext.sodalive.v2.main.home.ui.HomeFollowingCreatorAdapter"))
|
||||||
|
assertTrue(source.contains("import kr.co.vividnext.sodalive.v2.main.home.ui.HomeFollowingLiveAdapter"))
|
||||||
|
assertTrue(source.contains("import kr.co.vividnext.sodalive.v2.main.home.ui.HomeFollowingChatAdapter"))
|
||||||
|
assertTrue(source.contains("import kr.co.vividnext.sodalive.v2.main.home.ui.HomeFollowingScheduleAdapter"))
|
||||||
|
assertTrue(source.contains("import kr.co.vividnext.sodalive.v2.main.home.ui.HomeFollowingNewsAdapter"))
|
||||||
|
assertTrue(source.contains("HomeFollowingCreatorAdapter"))
|
||||||
|
assertTrue(source.contains("HomeFollowingLiveAdapter"))
|
||||||
|
assertTrue(source.contains("HomeFollowingChatAdapter"))
|
||||||
|
assertTrue(source.contains("HomeFollowingScheduleAdapter"))
|
||||||
|
assertTrue(source.contains("HomeFollowingNewsAdapter"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `following tab branch shows following content and hides other home surfaces`() {
|
||||||
|
val source = homeMainFragmentSource()
|
||||||
|
val branch = source.substringAfter("HOME_TAB_FOLLOWING ->")
|
||||||
|
.substringBefore("}\n }")
|
||||||
|
|
||||||
|
assertTrue(branch.contains("binding.nsvHomeFollowingContent.visibility = View.VISIBLE"))
|
||||||
|
assertTrue(branch.contains("binding.nsvHomeRecommendationContent.visibility = View.GONE"))
|
||||||
|
assertTrue(branch.contains("binding.rvHomeCreatorRankings.visibility = View.GONE"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `following tab loads following content only once`() {
|
||||||
|
val source = homeMainFragmentSource()
|
||||||
|
|
||||||
|
assertTrue(source.contains("private var hasLoadedFollowing = false"))
|
||||||
|
assertTrue(source.contains("if (!hasLoadedFollowing)"))
|
||||||
|
assertTrue(source.contains("hasLoadedFollowing = true"))
|
||||||
|
assertTrue(source.contains("homeFollowingViewModel.loadFollowing()"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `following section chevrons call callback without starting activity`() {
|
||||||
|
val source = homeMainFragmentSource()
|
||||||
|
val callback = source.substringAfter("private fun onFollowingSectionMoreClick")
|
||||||
|
.substringBefore("\n private fun")
|
||||||
|
|
||||||
|
assertTrue(source.contains("onFollowingSectionMoreClick("))
|
||||||
|
assertTrue(source.contains("binding.viewHomeFollowingCreatorsTitle"))
|
||||||
|
assertTrue(source.contains("binding.viewHomeFollowingOnAirTitle"))
|
||||||
|
assertTrue(source.contains("binding.viewHomeFollowingRecentChatsTitle"))
|
||||||
|
assertTrue(source.contains("binding.viewHomeFollowingMonthlySchedulesTitle"))
|
||||||
|
assertTrue(source.contains("binding.viewHomeFollowingRecentNewsTitle"))
|
||||||
|
assertTrue(source.contains("setOnClickListener"))
|
||||||
|
assertTrue(source.contains("onFollowingSectionMoreClick(HomeFollowingSection."))
|
||||||
|
assertFalse(callback.contains("startActivity"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `following state binding clears or binds each following section`() {
|
||||||
|
val source = homeMainFragmentSource()
|
||||||
|
|
||||||
|
assertTrue(source.contains("followingStateLiveData.observe(viewLifecycleOwner)"))
|
||||||
|
assertTrue(source.contains("is HomeFollowingUiState.Content -> bindHomeFollowingContent(state)"))
|
||||||
|
assertTrue(source.contains("HomeFollowingUiState.LoginRequired,"))
|
||||||
|
assertTrue(source.contains("HomeFollowingUiState.Empty,"))
|
||||||
|
assertTrue(source.contains("is HomeFollowingUiState.Error -> bindHomeFollowingEmpty()"))
|
||||||
|
assertTrue(source.contains("followingCreatorAdapter.submitItems(emptyList())"))
|
||||||
|
assertTrue(source.contains("followingLiveAdapter.submitItems(emptyList())"))
|
||||||
|
assertTrue(source.contains("followingChatAdapter.submitItems(emptyList())"))
|
||||||
|
assertTrue(source.contains("followingScheduleAdapter.submitItems(emptyList())"))
|
||||||
|
assertTrue(source.contains("followingNewsAdapter.submitItems(emptyList())"))
|
||||||
|
assertTrue(source.contains("bindFollowingCreatorSection(content.followingCreators)"))
|
||||||
|
assertTrue(source.contains("bindFollowingLiveSection(content.onAirLives)"))
|
||||||
|
assertTrue(source.contains("bindFollowingChatSection(content.recentChats)"))
|
||||||
|
assertTrue(source.contains("bindFollowingScheduleSection(content.monthlySchedules)"))
|
||||||
|
assertTrue(source.contains("bindFollowingNewsSection(content.recentNews)"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `following adapters bind figma required item fields`() {
|
||||||
|
val liveAdapter = projectFile(
|
||||||
|
"app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/ui/HomeFollowingLiveAdapter.kt"
|
||||||
|
).readText()
|
||||||
|
val liveLayout = projectFile("app/src/main/res/layout/item_home_following_live.xml").readText()
|
||||||
|
val scheduleAdapter = projectFile(
|
||||||
|
"app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/ui/HomeFollowingScheduleAdapter.kt"
|
||||||
|
).readText()
|
||||||
|
val scheduleLayout = projectFile("app/src/main/res/layout/item_home_following_schedule.xml").readText()
|
||||||
|
val newsAdapter = projectFile(
|
||||||
|
"app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/ui/HomeFollowingNewsAdapter.kt"
|
||||||
|
).readText()
|
||||||
|
val newsLayout = projectFile("app/src/main/res/layout/item_home_following_news_content.xml").readText()
|
||||||
|
|
||||||
|
assertTrue(liveLayout.contains("@+id/tv_home_following_live_started_at"))
|
||||||
|
assertTrue(liveAdapter.contains("startedAtText.text"))
|
||||||
|
assertTrue(liveAdapter.contains("item.startedAtUtc"))
|
||||||
|
|
||||||
|
assertTrue(scheduleLayout.contains("@+id/iv_home_following_schedule_creator_profile"))
|
||||||
|
assertTrue(scheduleLayout.contains("@+id/tv_home_following_schedule_type"))
|
||||||
|
assertTrue(scheduleAdapter.contains("profileImage.loadHomeCreatorProfileImage(item.creatorProfileImageUrl)"))
|
||||||
|
assertTrue(scheduleAdapter.contains("typeText.setText(item.typeLabelResId)"))
|
||||||
|
assertTrue(scheduleAdapter.contains("item.isOnAir"))
|
||||||
|
assertTrue(scheduleAdapter.contains("R.string.screen_home_following_on_air"))
|
||||||
|
|
||||||
|
assertTrue(newsLayout.contains("@+id/tv_home_following_news_label"))
|
||||||
|
assertTrue(newsLayout.contains("@+id/tv_home_following_news_title"))
|
||||||
|
assertTrue(newsAdapter.contains("labelText.setText(content.labelResId)"))
|
||||||
|
assertTrue(newsAdapter.contains("titleText.text = content.title"))
|
||||||
|
assertTrue(newsAdapter.contains("createdAtText.text = content.visibleFromText"))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun homeMainFragmentSource(): String {
|
||||||
|
return projectFile("app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/HomeMainFragment.kt").readText()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun homeMainLayoutSource(): String {
|
||||||
|
return projectFile("app/src/main/res/layout/fragment_v2_main_home.xml").readText()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun projectFile(relativePath: String): File {
|
||||||
|
val candidates = listOf(File(relativePath), File("../$relativePath"))
|
||||||
|
return candidates.first { it.exists() }.canonicalFile
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user