완결 시리즈

- 페이지 추가
This commit is contained in:
klaus 2025-02-14 19:09:37 +09:00
parent ef32eb70dd
commit d612bbb0f2
8 changed files with 234 additions and 0 deletions

View File

@ -154,6 +154,7 @@
<activity android:name=".audio_content.main.v2.AudioContentMainActivity" /> <activity android:name=".audio_content.main.v2.AudioContentMainActivity" />
<activity android:name=".audio_content.main.v2.series.origianl_audio_drama.OriginalAudioDramaContentAllActivity" /> <activity android:name=".audio_content.main.v2.series.origianl_audio_drama.OriginalAudioDramaContentAllActivity" />
<activity android:name=".audio_content.main.v2.series.completed.CompletedSeriesActivity" />
<activity android:name=".audio_content.main.v2.free.introduce_creator.IntroduceCreatorActivity" /> <activity android:name=".audio_content.main.v2.free.introduce_creator.IntroduceCreatorActivity" />
<activity android:name=".mypage.alarm.AlarmListActivity" /> <activity android:name=".mypage.alarm.AlarmListActivity" />

View File

@ -283,6 +283,13 @@ interface AudioContentApi {
@Header("Authorization") authHeader: String @Header("Authorization") authHeader: String
): Single<ApiResponse<List<GetSeriesListResponse.SeriesListItem>>> ): Single<ApiResponse<List<GetSeriesListResponse.SeriesListItem>>>
@GET("/v2/audio-content/main/series/completed-monthly-rank")
fun getCompletedSeries(
@Query("page") page: Int,
@Query("size") size: Int,
@Header("Authorization") authHeader: String
): Single<ApiResponse<GetSeriesListResponse>>
@GET("/v2/audio-content/main/content") @GET("/v2/audio-content/main/content")
fun getContentMainContent( fun getContentMainContent(
@Header("Authorization") authHeader: String @Header("Authorization") authHeader: String

View File

@ -18,6 +18,7 @@ import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.audio_content.main.AudioContentBannerType import kr.co.vividnext.sodalive.audio_content.main.AudioContentBannerType
import kr.co.vividnext.sodalive.audio_content.main.banner.AudioContentMainBannerAdapter import kr.co.vividnext.sodalive.audio_content.main.banner.AudioContentMainBannerAdapter
import kr.co.vividnext.sodalive.audio_content.main.v2.ContentRankCreatorAdapter import kr.co.vividnext.sodalive.audio_content.main.v2.ContentRankCreatorAdapter
import kr.co.vividnext.sodalive.audio_content.main.v2.series.completed.CompletedSeriesActivity
import kr.co.vividnext.sodalive.audio_content.main.v2.series.curation.AudioContentMainSeriesCurationAdapter import kr.co.vividnext.sodalive.audio_content.main.v2.series.curation.AudioContentMainSeriesCurationAdapter
import kr.co.vividnext.sodalive.audio_content.main.v2.series.new_series.AudioContentMainNewSeriesAdapter import kr.co.vividnext.sodalive.audio_content.main.v2.series.new_series.AudioContentMainNewSeriesAdapter
import kr.co.vividnext.sodalive.audio_content.main.v2.series.origianl_audio_drama.AudioContentMainTabSeriesOriginalAudioDramaAdapter import kr.co.vividnext.sodalive.audio_content.main.v2.series.origianl_audio_drama.AudioContentMainTabSeriesOriginalAudioDramaAdapter
@ -456,6 +457,15 @@ class AudioContentMainTabSeriesFragment : BaseFragment<FragmentAudioContentMainT
} }
private fun setupCompleteSeries() { private fun setupCompleteSeries() {
binding.ivCompleteSeriesAll.setOnClickListener {
startActivity(
Intent(
requireContext(),
CompletedSeriesActivity::class.java
)
)
}
completedSeriesAdapter = UserProfileSeriesListAdapter( completedSeriesAdapter = UserProfileSeriesListAdapter(
onClickItem = { onClickItem = {
startActivity( startActivity(

View File

@ -13,4 +13,14 @@ class AudioContentMainTabSeriesRepository(private val api: AudioContentApi) {
creatorId: Long, creatorId: Long,
token: String token: String
) = api.getRecommendSeriesByCreator(creatorId, authHeader = token) ) = api.getRecommendSeriesByCreator(creatorId, authHeader = token)
fun getCompletedSeries(
page: Int,
size: Int,
token: String
) = api.getCompletedSeries(
page = page - 1,
size = size,
authHeader = token
)
} }

View File

@ -0,0 +1,109 @@
package kr.co.vividnext.sodalive.audio_content.main.v2.series.completed
import android.content.Intent
import android.os.Bundle
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import kr.co.vividnext.sodalive.audio_content.series.SeriesListAdapter
import kr.co.vividnext.sodalive.audio_content.series.detail.SeriesDetailActivity
import kr.co.vividnext.sodalive.base.BaseActivity
import kr.co.vividnext.sodalive.common.Constants
import kr.co.vividnext.sodalive.common.DifferentSpacingItemDecoration
import kr.co.vividnext.sodalive.common.LoadingDialog
import kr.co.vividnext.sodalive.databinding.ActivityCompletedSeriesBinding
import kr.co.vividnext.sodalive.extensions.dpToPx
import org.koin.android.ext.android.inject
import kotlin.math.roundToInt
class CompletedSeriesActivity : BaseActivity<ActivityCompletedSeriesBinding>(
ActivityCompletedSeriesBinding::inflate
) {
private val viewModel: CompletedSeriesViewModel by inject()
private lateinit var loadingDialog: LoadingDialog
private lateinit var adapter: SeriesListAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
bindData()
viewModel.getCompletedSeries()
}
override fun setupView() {
loadingDialog = LoadingDialog(this, layoutInflater)
binding.toolbar.tvBack.text = "완결 시리즈"
binding.toolbar.tvBack.setOnClickListener { finish() }
setupCompletedSeriesListView()
}
private fun setupCompletedSeriesListView() {
val spacing = 13.3f.dpToPx().roundToInt()
adapter = SeriesListAdapter(
itemWidth = ((screenWidth - spacing * 3) / 2f).roundToInt(),
onClickItem = {
startActivity(
Intent(applicationContext, SeriesDetailActivity::class.java).apply {
putExtra(Constants.EXTRA_SERIES_ID, it)
}
)
},
onClickCreator = {},
isVisibleCreator = false
)
val spanCount = 2
val recyclerView = binding.rvSeries
recyclerView.layoutManager = GridLayoutManager(this, spanCount)
recyclerView.addItemDecoration(
DifferentSpacingItemDecoration(
spanCount = spanCount,
horizontalSpacing = spacing,
verticalSpacing = spacing,
includeEdge = true
)
)
recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
val lastVisibleItemPosition = (recyclerView.layoutManager as LinearLayoutManager?)!!
.findLastCompletelyVisibleItemPosition()
val itemTotalCount = recyclerView.adapter!!.itemCount - 1
// 스크롤이 끝에 도달했는지 확인
if (!recyclerView.canScrollVertically(1) &&
lastVisibleItemPosition == itemTotalCount
) {
viewModel.getCompletedSeries()
}
}
})
recyclerView.adapter = adapter
}
private fun bindData() {
viewModel.toastLiveData.observe(this) {
it?.let { showToast(it) }
}
viewModel.isLoading.observe(this) {
if (it) {
loadingDialog.show(screenWidth)
} else {
loadingDialog.dismiss()
}
}
viewModel.completedSeriesLiveData.observe(this) {
adapter.addItems(it)
}
}
}

View File

@ -0,0 +1,75 @@
package kr.co.vividnext.sodalive.audio_content.main.v2.series.completed
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.main.v2.series.AudioContentMainTabSeriesRepository
import kr.co.vividnext.sodalive.audio_content.series.GetSeriesListResponse
import kr.co.vividnext.sodalive.base.BaseViewModel
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
class CompletedSeriesViewModel(
private val repository: AudioContentMainTabSeriesRepository
) : BaseViewModel() {
private val _toastLiveData = MutableLiveData<String?>()
val toastLiveData: LiveData<String?>
get() = _toastLiveData
private var _isLoading = MutableLiveData(false)
val isLoading: LiveData<Boolean>
get() = _isLoading
private var _completedSeriesLiveData =
MutableLiveData<List<GetSeriesListResponse.SeriesListItem>>()
val completedSeriesLiveData: LiveData<List<GetSeriesListResponse.SeriesListItem>>
get() = _completedSeriesLiveData
var isLast = false
var page = 1
private val size = 20
fun getCompletedSeries() {
if (!_isLoading.value!! && !isLast) {
_isLoading.value = true
compositeDisposable.add(
repository.getCompletedSeries(
page = page,
size = size,
token = "Bearer ${SharedPreferenceManager.token}"
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
if (it.success && it.data != null) {
page += 1
if (it.data.items.isNotEmpty()) {
_completedSeriesLiveData.value = it.data.items
} else {
isLast = true
}
} else {
if (it.message != null) {
_toastLiveData.value = it.message
} else {
_toastLiveData.value = "알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
}
}
_isLoading.value = false
},
{
_isLoading.value = false
it.message?.let { message -> Logger.e(message) }
_toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
}
)
)
}
}
}

View File

@ -38,6 +38,7 @@ import kr.co.vividnext.sodalive.audio_content.main.v2.replay.AudioContentMainTab
import kr.co.vividnext.sodalive.audio_content.main.v2.replay.AudioContentMainTabReplayViewModel import kr.co.vividnext.sodalive.audio_content.main.v2.replay.AudioContentMainTabReplayViewModel
import kr.co.vividnext.sodalive.audio_content.main.v2.series.AudioContentMainTabSeriesRepository import kr.co.vividnext.sodalive.audio_content.main.v2.series.AudioContentMainTabSeriesRepository
import kr.co.vividnext.sodalive.audio_content.main.v2.series.AudioContentMainTabSeriesViewModel import kr.co.vividnext.sodalive.audio_content.main.v2.series.AudioContentMainTabSeriesViewModel
import kr.co.vividnext.sodalive.audio_content.main.v2.series.completed.CompletedSeriesViewModel
import kr.co.vividnext.sodalive.audio_content.main.v2.series.origianl_audio_drama.OriginalAudioDramaContentAllRepository import kr.co.vividnext.sodalive.audio_content.main.v2.series.origianl_audio_drama.OriginalAudioDramaContentAllRepository
import kr.co.vividnext.sodalive.audio_content.main.v2.series.origianl_audio_drama.OriginalAudioDramaContentAllViewModel import kr.co.vividnext.sodalive.audio_content.main.v2.series.origianl_audio_drama.OriginalAudioDramaContentAllViewModel
import kr.co.vividnext.sodalive.audio_content.modify.AudioContentModifyViewModel import kr.co.vividnext.sodalive.audio_content.modify.AudioContentModifyViewModel
@ -316,6 +317,7 @@ class AppDI(private val context: Context, isDebugMode: Boolean) {
viewModel { AudioContentMainTabFreeViewModel(get()) } viewModel { AudioContentMainTabFreeViewModel(get()) }
viewModel { OriginalAudioDramaContentAllViewModel(get()) } viewModel { OriginalAudioDramaContentAllViewModel(get()) }
viewModel { IntroduceCreatorViewModel(get()) } viewModel { IntroduceCreatorViewModel(get()) }
viewModel { CompletedSeriesViewModel(get()) }
} }
private val repositoryModule = module { private val repositoryModule = module {

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<include
android:id="@+id/toolbar"
layout="@layout/detail_toolbar" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_series"
android:layout_width="0dp"
android:layout_height="0dp"
android:clipToPadding="false"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/toolbar" />
</androidx.constraintlayout.widget.ConstraintLayout>