From a2262eff3f95a70205b517f954903047e59aeaf9 Mon Sep 17 00:00:00 2001 From: klaus Date: Tue, 11 Nov 2025 23:12:37 +0900 Subject: [PATCH] =?UTF-8?q?feat(home):=20=EB=B3=B4=EC=98=A8=20=EC=A3=BC?= =?UTF-8?q?=EA=B0=84=20=EC=B0=A8=ED=8A=B8=20=EC=BD=98=ED=85=90=EC=B8=A0=20?= =?UTF-8?q?=EC=A0=95=EB=A0=AC=20=EA=B8=B0=EC=A4=80=20=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?-=20=EB=A7=A4=EC=B6=9C,=20=ED=8C=90=EB=A7=A4=EB=9F=89,=20?= =?UTF-8?q?=EB=8C=93=EA=B8=80=20=EC=88=98,=20=EC=A2=8B=EC=95=84=EC=9A=94?= =?UTF-8?q?=20=EC=88=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../home/ContentRankingSortAdapter.kt | 59 +++++++++++++++++++ .../sodalive/home/ContentRankingSortType.kt | 18 ++++++ .../kr/co/vividnext/sodalive/home/HomeApi.kt | 10 ++++ .../vividnext/sodalive/home/HomeFragment.kt | 42 +++++++++++-- .../vividnext/sodalive/home/HomeRepository.kt | 10 ++++ .../vividnext/sodalive/home/HomeViewModel.kt | 29 +++++++++ app/src/main/res/layout/fragment_home.xml | 13 +++- 7 files changed, 173 insertions(+), 8 deletions(-) create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/home/ContentRankingSortAdapter.kt create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/home/ContentRankingSortType.kt diff --git a/app/src/main/java/kr/co/vividnext/sodalive/home/ContentRankingSortAdapter.kt b/app/src/main/java/kr/co/vividnext/sodalive/home/ContentRankingSortAdapter.kt new file mode 100644 index 00000000..fd0e8193 --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/home/ContentRankingSortAdapter.kt @@ -0,0 +1,59 @@ +package kr.co.vividnext.sodalive.home + +import android.annotation.SuppressLint +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import kr.co.vividnext.sodalive.R +import kr.co.vividnext.sodalive.databinding.ItemHomeContentThemeBinding + +/** + * 보온 주간 차트 정렬 선택 어댑터 + * - 최신 콘텐츠 테마 선택 UI와 동일한 스타일 재사용 + */ +class ContentRankingSortAdapter( + private val onClickSort: (ContentRankingSortType) -> Unit +) : RecyclerView.Adapter() { + + private val sorts = ContentRankingSortType.entries + private var selected: ContentRankingSortType = ContentRankingSortType.REVENUE + + inner class ViewHolder( + private val binding: ItemHomeContentThemeBinding + ) : RecyclerView.ViewHolder(binding.root) { + @SuppressLint("NotifyDataSetChanged") + fun bind(type: ContentRankingSortType) { + binding.tvTheme.text = type.toKoreanLabel() + if (type == selected) { + binding.tvTheme.setBackgroundResource(R.drawable.bg_round_corner_999_3bb9f1) + } else { + binding.tvTheme.setBackgroundResource(R.drawable.bg_round_corner_999_263238) + } + + binding.root.setOnClickListener { + if (selected != type) { + selected = type + notifyDataSetChanged() + onClickSort(type) + } + } + } + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder( + ItemHomeContentThemeBinding.inflate(LayoutInflater.from(parent.context), parent, false) + ) + + override fun getItemCount() = sorts.size + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + holder.bind(sorts[position]) + } +} + +private fun ContentRankingSortType.toKoreanLabel(): String = when (this) { + ContentRankingSortType.REVENUE -> "매출" + ContentRankingSortType.SALES_COUNT -> "판매량" + ContentRankingSortType.COMMENT_COUNT -> "댓글" + ContentRankingSortType.LIKE_COUNT -> "좋아요" +} diff --git a/app/src/main/java/kr/co/vividnext/sodalive/home/ContentRankingSortType.kt b/app/src/main/java/kr/co/vividnext/sodalive/home/ContentRankingSortType.kt new file mode 100644 index 00000000..d1892a46 --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/home/ContentRankingSortType.kt @@ -0,0 +1,18 @@ +package kr.co.vividnext.sodalive.home + +/** + * 콘텐츠 랭킹 정렬 기준 + */ +enum class ContentRankingSortType { + // 매출 + REVENUE, + + // 판매량 + SALES_COUNT, + + // 댓글 수 + COMMENT_COUNT, + + // 좋아요 수 + LIKE_COUNT +} diff --git a/app/src/main/java/kr/co/vividnext/sodalive/home/HomeApi.kt b/app/src/main/java/kr/co/vividnext/sodalive/home/HomeApi.kt index dd34b4b6..3faae189 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/home/HomeApi.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/home/HomeApi.kt @@ -1,11 +1,13 @@ package kr.co.vividnext.sodalive.home import io.reactivex.rxjava3.core.Single +import kr.co.vividnext.sodalive.audio_content.main.GetAudioContentRankingItem import kr.co.vividnext.sodalive.audio_content.series.GetSeriesListResponse import kr.co.vividnext.sodalive.common.ApiResponse import kr.co.vividnext.sodalive.settings.ContentType import retrofit2.http.GET import retrofit2.http.Header +import retrofit2.http.Path import retrofit2.http.Query interface HomeApi { @@ -39,4 +41,12 @@ interface HomeApi { @Query("contentType") contentType: ContentType, @Header("Authorization") authHeader: String ): Single>> + + @GET("/api/home/content-ranking") + fun getContentRankingBySort( + @Query("sort") sort: ContentRankingSortType, + @Query("isAdultContentVisible") isAdultContentVisible: Boolean, + @Query("contentType") contentType: ContentType, + @Header("Authorization") authHeader: String + ): Single>> } diff --git a/app/src/main/java/kr/co/vividnext/sodalive/home/HomeFragment.kt b/app/src/main/java/kr/co/vividnext/sodalive/home/HomeFragment.kt index 587752d4..42420138 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/home/HomeFragment.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/home/HomeFragment.kt @@ -869,6 +869,41 @@ class HomeFragment : BaseFragment(FragmentHomeBinding::infl ) binding.tvWeeklyChart.text = spSectionTitle + // 정렬 선택 RecyclerView 설정 + val sortAdapter = ContentRankingSortAdapter { + viewModel.getContentRanking(it) + } + binding.rvWeeklyChartSort.apply { + layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false) + addItemDecoration(object : RecyclerView.ItemDecoration() { + override fun getItemOffsets( + outRect: Rect, + view: View, + parent: RecyclerView, + state: RecyclerView.State + ) { + super.getItemOffsets(outRect, view, parent, state) + when (parent.getChildAdapterPosition(view)) { + 0 -> { + outRect.left = 0 + outRect.right = 8f.dpToPx().toInt() + } + + sortAdapter.itemCount - 1 -> { + outRect.left = 8f.dpToPx().toInt() + outRect.right = 0 + } + + else -> { + outRect.left = 8f.dpToPx().toInt() + outRect.right = 8f.dpToPx().toInt() + } + } + } + }) + adapter = sortAdapter + } + weelyChartAdapter = HomeWeeklyChartAdapter( width = screenWidth, onClickItem = { @@ -935,12 +970,7 @@ class HomeFragment : BaseFragment(FragmentHomeBinding::infl recyclerView.adapter = weelyChartAdapter viewModel.contentRankingLiveData.observe(viewLifecycleOwner) { - if (it.isNotEmpty()) { - binding.llWeeklyChart.visibility = View.VISIBLE - weelyChartAdapter.addItems(it) - } else { - binding.llWeeklyChart.visibility = View.GONE - } + weelyChartAdapter.addItems(it) } } diff --git a/app/src/main/java/kr/co/vividnext/sodalive/home/HomeRepository.kt b/app/src/main/java/kr/co/vividnext/sodalive/home/HomeRepository.kt index 13dd7f3c..286ff727 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/home/HomeRepository.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/home/HomeRepository.kt @@ -33,4 +33,14 @@ class HomeRepository(private val api: HomeApi) { contentType = ContentType.entries[SharedPreferenceManager.contentPreference], authHeader = token ) + + fun getContentRanking( + sortType: ContentRankingSortType, + token: String + ) = api.getContentRankingBySort( + sort = sortType, + isAdultContentVisible = SharedPreferenceManager.isAdultContentVisible, + contentType = ContentType.entries[SharedPreferenceManager.contentPreference], + authHeader = token + ) } diff --git a/app/src/main/java/kr/co/vividnext/sodalive/home/HomeViewModel.kt b/app/src/main/java/kr/co/vividnext/sodalive/home/HomeViewModel.kt index 7afcac94..5873c3ae 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/home/HomeViewModel.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/home/HomeViewModel.kt @@ -113,6 +113,9 @@ class HomeViewModel( data.pointAvailableContentList _recommendChannelListLiveData.value = data.recommendChannelList _recommendContentListLiveData.value = data.recommendContentList + + // 홈 진입 시 기본 정렬(REVENUE) 랭킹을 최신화 + getContentRanking(ContentRankingSortType.REVENUE) } else { if (it.message != null) { _toastLiveData.postValue(it.message) @@ -132,6 +135,32 @@ class HomeViewModel( ) } + fun getContentRanking(sortType: ContentRankingSortType) { + compositeDisposable.add( + repository.getContentRanking(sortType, token = "Bearer ${SharedPreferenceManager.token}") + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + { + val data = it.data + if (it.success && data != null) { + _contentRankingLiveData.value = data + } else { + if (it.message != null) { + _toastLiveData.postValue(it.message) + } else { + _toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.") + } + } + }, + { + it.message?.let { message -> Logger.e(message) } + _toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.") + } + ) + ) + } + fun refreshRecommendContents() { _isLoading.value = true compositeDisposable.add( diff --git a/app/src/main/res/layout/fragment_home.xml b/app/src/main/res/layout/fragment_home.xml index 44778198..5b462388 100644 --- a/app/src/main/res/layout/fragment_home.xml +++ b/app/src/main/res/layout/fragment_home.xml @@ -313,8 +313,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="48dp" - android:orientation="vertical" - android:visibility="gone"> + android:orientation="vertical"> + + +