콘텐츠 메인

- 인기 크리에이터 추가
This commit is contained in:
klaus 2025-01-05 17:21:11 +09:00
parent 2bec9d4595
commit 9cafb13b50
6 changed files with 227 additions and 4 deletions

View File

@ -6,6 +6,7 @@ import kr.co.vividnext.sodalive.audio_content.donation.AudioContentDonationReque
import kr.co.vividnext.sodalive.audio_content.order.OrderRequest
import kr.co.vividnext.sodalive.audio_content.order.OrderType
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
import kr.co.vividnext.sodalive.explorer.ExplorerApi
import kr.co.vividnext.sodalive.settings.ContentType
import kr.co.vividnext.sodalive.user.CreatorFollowRequestRequest
import kr.co.vividnext.sodalive.user.UserApi
@ -16,7 +17,8 @@ import java.util.TimeZone
class AudioContentRepository(
private val api: AudioContentApi,
private val userApi: UserApi,
private val categoryApi: CategoryApi
private val categoryApi: CategoryApi,
private val explorerApi: ExplorerApi
) {
fun getAudioContentListByCurationId(
curationId: Long,
@ -232,4 +234,6 @@ class AudioContentRepository(
sort = sort,
authHeader = token
)
fun getCreatorRank(token: String) = explorerApi.getCreatorRank(authHeader = token)
}

View File

@ -2,16 +2,23 @@ package kr.co.vividnext.sodalive.audio_content.main
import android.annotation.SuppressLint
import android.content.Intent
import android.graphics.Color
import android.graphics.Rect
import android.net.Uri
import android.os.Bundle
import android.text.SpannableString
import android.text.Spanned
import android.text.style.ForegroundColorSpan
import android.view.View
import android.widget.LinearLayout
import android.widget.Toast
import androidx.annotation.OptIn
import androidx.core.content.ContextCompat
import androidx.media3.common.util.UnstableApi
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.orhanobut.logger.Logger
import com.zhpan.bannerview.BaseBannerAdapter
import com.zhpan.indicator.enums.IndicatorSlideMode
import com.zhpan.indicator.enums.IndicatorStyle
@ -29,16 +36,17 @@ import kr.co.vividnext.sodalive.audio_content.main.curation.AudioContentMainCura
import kr.co.vividnext.sodalive.audio_content.main.new_content.AudioContentMainNewContentThemeAdapter
import kr.co.vividnext.sodalive.audio_content.main.new_content.AudioContentMainNewContentViewModel
import kr.co.vividnext.sodalive.audio_content.main.order.AudioContentMainOrderListViewModel
import kr.co.vividnext.sodalive.audio_content.main.ranking.AudioContentMainCreatorRankingViewModel
import kr.co.vividnext.sodalive.audio_content.main.ranking.AudioContentMainRankingAdapter
import kr.co.vividnext.sodalive.audio_content.main.ranking.AudioContentMainRankingViewModel
import kr.co.vividnext.sodalive.audio_content.main.recommend_series.AudioContentMainRecommendSeriesViewModel
import kr.co.vividnext.sodalive.audio_content.order.AudioContentOrderListActivity
import kr.co.vividnext.sodalive.audio_content.series.detail.SeriesDetailActivity
import kr.co.vividnext.sodalive.audio_content.upload.AudioContentUploadActivity
import kr.co.vividnext.sodalive.base.BaseFragment
import kr.co.vividnext.sodalive.common.Constants
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
import kr.co.vividnext.sodalive.databinding.FragmentAudioContentMainBinding
import kr.co.vividnext.sodalive.explorer.ExplorerSectionAdapter
import kr.co.vividnext.sodalive.explorer.profile.UserProfileActivity
import kr.co.vividnext.sodalive.explorer.profile.series.UserProfileSeriesListAdapter
import kr.co.vividnext.sodalive.extensions.dpToPx
@ -48,9 +56,13 @@ import kr.co.vividnext.sodalive.settings.notification.MemberRole
import org.koin.android.ext.android.inject
import kotlin.math.roundToInt
@OptIn(UnstableApi::class)
class AudioContentMainFragment : BaseFragment<FragmentAudioContentMainBinding>(
FragmentAudioContentMainBinding::inflate
) {
private val creatorRankViewModel: AudioContentMainCreatorRankingViewModel by inject()
private lateinit var creatorRankAdaptor: ExplorerSectionAdapter
private val recommendSeriesViewModel: AudioContentMainRecommendSeriesViewModel by inject()
private lateinit var seriesAdapter: UserProfileSeriesListAdapter
@ -78,6 +90,7 @@ class AudioContentMainFragment : BaseFragment<FragmentAudioContentMainBinding>(
curationViewModel.getCurationList()
bannerViewModel.getMainBannerList()
newContentViewModel.getThemeList()
creatorRankViewModel.getCreatorRank()
newContentViewModel.getNewContentOfTheme("전체")
contentRankingViewModel.getContentRanking()
contentRankingViewModel.getContentRankingSortType()
@ -99,6 +112,7 @@ class AudioContentMainFragment : BaseFragment<FragmentAudioContentMainBinding>(
binding.llUploadContent.visibility = View.GONE
}
setupCreatorRank()
setupRecommendSeries()
setupBanner()
setupOrderList()
@ -143,6 +157,95 @@ class AudioContentMainFragment : BaseFragment<FragmentAudioContentMainBinding>(
}
}
private fun setupCreatorRank() {
creatorRankAdaptor = ExplorerSectionAdapter(
onClickItem = {
startActivity(
Intent(requireContext(), UserProfileActivity::class.java).apply {
putExtra(Constants.EXTRA_USER_ID, it)
}
)
},
isVisibleRanking = true
)
binding.rvCreatorRank.layoutManager = LinearLayoutManager(
context,
LinearLayoutManager.HORIZONTAL,
false
)
binding.rvCreatorRank.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 = 6.7f.dpToPx().toInt()
}
creatorRankAdaptor.itemCount - 1 -> {
outRect.left = 6.7f.dpToPx().toInt()
outRect.right = 0
}
else -> {
outRect.left = 6.7f.dpToPx().toInt()
outRect.right = 6.7f.dpToPx().toInt()
}
}
}
})
binding.rvCreatorRank.adapter = creatorRankAdaptor
creatorRankViewModel.creatorRankLiveData.observe(viewLifecycleOwner) {
binding.tvDesc.text = it.desc
binding.tvCreatorRankTitle.text = if (
!it.coloredTitle.isNullOrBlank() &&
!it.color.isNullOrBlank()
) {
val spStr = SpannableString(it.title)
try {
spStr.setSpan(
ForegroundColorSpan(
Color.parseColor("#${it.color}")
),
it.title.indexOf(it.coloredTitle),
it.title.indexOf(it.coloredTitle) + it.coloredTitle.length,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
)
spStr
} catch (e: IllegalArgumentException) {
it.title
}
} else {
it.title
}
creatorRankAdaptor.addItems(it.creators)
if (creatorRankAdaptor.itemCount <= 0 && it.creators.isEmpty()) {
binding.llCreatorRank.visibility = View.GONE
binding.rvCreatorRank.visibility = View.GONE
} else {
binding.llCreatorRank.visibility = View.VISIBLE
binding.rvCreatorRank.visibility = View.VISIBLE
}
}
creatorRankViewModel.toastLiveData.observe(viewLifecycleOwner) {
it?.let { Toast.makeText(requireContext(), it, Toast.LENGTH_LONG).show() }
}
}
private fun setupRecommendSeries() {
seriesAdapter = UserProfileSeriesListAdapter(
onClickItem = {

View File

@ -0,0 +1,55 @@
package kr.co.vividnext.sodalive.audio_content.main.ranking
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.orhanobut.logger.Logger
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.schedulers.Schedulers
import kr.co.vividnext.sodalive.audio_content.AudioContentRepository
import kr.co.vividnext.sodalive.base.BaseViewModel
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
import kr.co.vividnext.sodalive.explorer.GetExplorerSectionResponse
class AudioContentMainCreatorRankingViewModel(
private val repository: AudioContentRepository
) : BaseViewModel() {
private val _toastLiveData = MutableLiveData<String?>()
val toastLiveData: LiveData<String?>
get() = _toastLiveData
private val _creatorRankLiveData = MutableLiveData<GetExplorerSectionResponse>()
val creatorRankLiveData: LiveData<GetExplorerSectionResponse>
get() = _creatorRankLiveData
fun getCreatorRank() {
compositeDisposable.add(
repository
.getCreatorRank(token = "Bearer ${SharedPreferenceManager.token}")
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
if (it.success && it.data != null) {
_creatorRankLiveData.value = it.data!!
} else {
if (it.message != null) {
_toastLiveData.postValue(it.message)
} else {
_toastLiveData.postValue(
"인기 크리에이터를 불러오지 못했습니다. 다시 시도해 주세요.\n" +
"계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
)
}
}
},
{
it.message?.let { message -> Logger.e(message) }
_toastLiveData.postValue(
"인기 크리에이터를 불러오지 못했습니다. 다시 시도해 주세요.\n" +
"계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
)
}
)
)
}
}

View File

@ -20,6 +20,7 @@ import kr.co.vividnext.sodalive.audio_content.main.banner.AudioContentMainBanner
import kr.co.vividnext.sodalive.audio_content.main.curation.AudioContentMainCurationViewModel
import kr.co.vividnext.sodalive.audio_content.main.new_content.AudioContentMainNewContentViewModel
import kr.co.vividnext.sodalive.audio_content.main.order.AudioContentMainOrderListViewModel
import kr.co.vividnext.sodalive.audio_content.main.ranking.AudioContentMainCreatorRankingViewModel
import kr.co.vividnext.sodalive.audio_content.main.ranking.AudioContentMainRankingViewModel
import kr.co.vividnext.sodalive.audio_content.main.recommend_series.AudioContentMainRecommendSeriesViewModel
import kr.co.vividnext.sodalive.audio_content.modify.AudioContentModifyViewModel
@ -288,6 +289,7 @@ class AppDI(private val context: Context, isDebugMode: Boolean) {
viewModel { AuditionViewModel(get()) }
viewModel { AuditionDetailViewModel(get()) }
viewModel { AuditionRoleDetailViewModel(get()) }
viewModel { AudioContentMainCreatorRankingViewModel(get()) }
}
private val repositoryModule = module {
@ -305,7 +307,7 @@ class AppDI(private val context: Context, isDebugMode: Boolean) {
factory { ExplorerRepository(get()) }
factory { MessageRepository(get()) }
factory { NoticeRepository(get()) }
factory { AudioContentRepository(get(), get(), get()) }
factory { AudioContentRepository(get(), get(), get(), get()) }
factory { AudioContentCommentRepository(get()) }
factory { PlaybackTrackingRepository(get()) }
factory { FollowingCreatorRepository(get()) }

View File

@ -25,6 +25,11 @@ interface ExplorerApi {
@Header("Authorization") authHeader: String
): Single<ApiResponse<GetExplorerResponse>>
@GET("/explorer/creator-rank")
fun getCreatorRank(
@Header("Authorization") authHeader: String
): Single<ApiResponse<GetExplorerSectionResponse>>
@GET("/explorer/search/channel")
fun searchChannel(
@Query("channel") channel: String,

View File

@ -64,6 +64,60 @@
android:layout_marginTop="6.7dp"
android:layout_marginBottom="26.7dp" />
<LinearLayout
android:id="@+id/ll_creator_rank"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="40dp"
android:orientation="vertical"
android:visibility="gone">
<TextView
android:id="@+id/tv_creator_rank_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fontFamily="@font/gmarket_sans_bold"
android:paddingHorizontal="13.3dp"
android:text="인기 급상승"
android:textColor="@color/color_eeeeee"
android:textSize="18.3sp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="13.3dp"
android:background="@color/color_222222"
android:gravity="center"
android:orientation="vertical"
android:paddingVertical="8dp">
<TextView
android:id="@+id/tv_desc"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/gmarket_sans_bold"
android:textColor="@color/color_eeeeee"
android:textSize="14.7sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:fontFamily="@font/gmarket_sans_light"
android:text="※ 인기 크리에이터의 순위는 매주 업데이트됩니다."
android:textColor="@color/color_bbbbbb"
android:textSize="13.3sp" />
</LinearLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_creator_rank"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="13.3dp"
android:clipToPadding="false"
android:paddingHorizontal="13.3dp" />
</LinearLayout>
<LinearLayout
android:id="@+id/ll_recommend_series"
android:layout_width="match_parent"
@ -136,8 +190,8 @@
android:layout_height="wrap_content"
android:drawablePadding="2.7dp"
android:fontFamily="@font/gmarket_sans_bold"
android:text="보이스 모닝콜"
android:gravity="center"
android:text="보이스 모닝콜"
android:textColor="@color/color_0057ff"
android:textSize="16.7sp"
app:drawableStartCompat="@drawable/ic_alarm_clock_blue" />