feat(ui): 캐릭터 탭
- 섹션별로 데이터가 있으면 보여주고 없으면 UI를 제거하도록 로직 추가
This commit is contained in:
@@ -1,5 +1,13 @@
|
|||||||
package kr.co.vividnext.sodalive.chat.character
|
package kr.co.vividnext.sodalive.chat.character
|
||||||
|
|
||||||
|
import kr.co.vividnext.sodalive.audio_content.main.GetAudioContentBannerResponse
|
||||||
|
import kr.co.vividnext.sodalive.chat.character.curation.CurationSection
|
||||||
|
import kr.co.vividnext.sodalive.chat.character.recent.RecentCharacter
|
||||||
|
|
||||||
data class CharacterHomeResponse(
|
data class CharacterHomeResponse(
|
||||||
val id: Long
|
val banners: List<GetAudioContentBannerResponse>,
|
||||||
|
val recentCharacters: List<RecentCharacter>,
|
||||||
|
val popularCharacters: List<Character>,
|
||||||
|
val newCharacters: List<Character>,
|
||||||
|
val curationSections: List<CurationSection>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ 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.series.detail.SeriesDetailActivity
|
import kr.co.vividnext.sodalive.audio_content.series.detail.SeriesDetailActivity
|
||||||
import kr.co.vividnext.sodalive.base.BaseFragment
|
import kr.co.vividnext.sodalive.base.BaseFragment
|
||||||
import kr.co.vividnext.sodalive.chat.character.curation.CurationSection
|
|
||||||
import kr.co.vividnext.sodalive.chat.character.curation.CurationSectionAdapter
|
import kr.co.vividnext.sodalive.chat.character.curation.CurationSectionAdapter
|
||||||
import kr.co.vividnext.sodalive.chat.character.recent.RecentCharacter
|
import kr.co.vividnext.sodalive.chat.character.recent.RecentCharacter
|
||||||
import kr.co.vividnext.sodalive.chat.character.recent.RecentCharacterAdapter
|
import kr.co.vividnext.sodalive.chat.character.recent.RecentCharacterAdapter
|
||||||
@@ -48,7 +47,6 @@ class CharacterTabFragment : BaseFragment<FragmentCharacterTabBinding>(
|
|||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
setupView()
|
setupView()
|
||||||
loadData()
|
|
||||||
|
|
||||||
viewModel.fetchData()
|
viewModel.fetchData()
|
||||||
}
|
}
|
||||||
@@ -135,6 +133,15 @@ class CharacterTabFragment : BaseFragment<FragmentCharacterTabBinding>(
|
|||||||
)
|
)
|
||||||
.setIndicatorSliderWidth(10f.dpToPx().toInt(), 10f.dpToPx().toInt())
|
.setIndicatorSliderWidth(10f.dpToPx().toInt(), 10f.dpToPx().toInt())
|
||||||
.setIndicatorHeight(10f.dpToPx().toInt())
|
.setIndicatorHeight(10f.dpToPx().toInt())
|
||||||
|
|
||||||
|
viewModel.bannerListLiveData.observe(viewLifecycleOwner) {
|
||||||
|
if (it.isNotEmpty()) {
|
||||||
|
binding.llBanner.visibility = View.VISIBLE
|
||||||
|
binding.bannerSlider.refreshData(it)
|
||||||
|
} else {
|
||||||
|
binding.llBanner.visibility = View.GONE
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupRecentCharactersRecyclerView() {
|
private fun setupRecentCharactersRecyclerView() {
|
||||||
@@ -180,6 +187,17 @@ class CharacterTabFragment : BaseFragment<FragmentCharacterTabBinding>(
|
|||||||
})
|
})
|
||||||
|
|
||||||
recyclerView.adapter = recentCharacterAdapter
|
recyclerView.adapter = recentCharacterAdapter
|
||||||
|
|
||||||
|
// 최근 대화한 캐릭터 LiveData 구독
|
||||||
|
viewModel.recentCharacters.observe(viewLifecycleOwner) {
|
||||||
|
if (it.isNotEmpty()) {
|
||||||
|
binding.llLatestCharacters.visibility = View.VISIBLE
|
||||||
|
recentCharacterAdapter.updateCharacters(it)
|
||||||
|
binding.tvLatestCharacterCount.text = it.size.toString()
|
||||||
|
} else {
|
||||||
|
binding.llLatestCharacters.visibility = View.GONE
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupPopularCharactersRecyclerView() {
|
private fun setupPopularCharactersRecyclerView() {
|
||||||
@@ -230,6 +248,16 @@ class CharacterTabFragment : BaseFragment<FragmentCharacterTabBinding>(
|
|||||||
|
|
||||||
binding.tvPopularCharacterAll.setOnClickListener {
|
binding.tvPopularCharacterAll.setOnClickListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 인기 캐릭터 LiveData 구독
|
||||||
|
viewModel.popularCharacters.observe(viewLifecycleOwner) {
|
||||||
|
if (it.isNotEmpty()) {
|
||||||
|
binding.llPopularCharacters.visibility = View.VISIBLE
|
||||||
|
popularCharacterAdapter.updateCharacters(it)
|
||||||
|
} else {
|
||||||
|
binding.llPopularCharacters.visibility = View.GONE
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupNewCharactersRecyclerView() {
|
private fun setupNewCharactersRecyclerView() {
|
||||||
@@ -280,6 +308,16 @@ class CharacterTabFragment : BaseFragment<FragmentCharacterTabBinding>(
|
|||||||
|
|
||||||
binding.tvNewCharacterAll.setOnClickListener {
|
binding.tvNewCharacterAll.setOnClickListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 신규 캐릭터 LiveData 구독
|
||||||
|
viewModel.newCharacters.observe(viewLifecycleOwner) {
|
||||||
|
if (it.isNotEmpty()) {
|
||||||
|
binding.llNewCharacters.visibility = View.VISIBLE
|
||||||
|
newCharacterAdapter.updateCharacters(it)
|
||||||
|
} else {
|
||||||
|
binding.llNewCharacters.visibility = View.GONE
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupCurationSectionsRecyclerView() {
|
private fun setupCurationSectionsRecyclerView() {
|
||||||
@@ -326,79 +364,18 @@ class CharacterTabFragment : BaseFragment<FragmentCharacterTabBinding>(
|
|||||||
})
|
})
|
||||||
|
|
||||||
recyclerView.adapter = curationSectionAdapter
|
recyclerView.adapter = curationSectionAdapter
|
||||||
|
|
||||||
|
// 큐레이션 섹션 LiveData 구독
|
||||||
|
viewModel.curationSections.observe(viewLifecycleOwner) {
|
||||||
|
if (it.isNotEmpty()) {
|
||||||
|
recyclerView.visibility = View.VISIBLE
|
||||||
|
curationSectionAdapter.updateSections(it)
|
||||||
|
} else {
|
||||||
|
recyclerView.visibility = View.GONE
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadData() {
|
|
||||||
// TODO: 실제 데이터 로딩 로직 구현
|
|
||||||
loadRecentCharacters()
|
|
||||||
loadPopularCharacters()
|
|
||||||
loadNewCharacters()
|
|
||||||
loadCurationSections()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun loadRecentCharacters() {
|
|
||||||
// TODO: 서버에서 최근 대화한 캐릭터 데이터 로드
|
|
||||||
val recentCharacters = listOf(
|
|
||||||
RecentCharacter("1", "Yubin...", ""),
|
|
||||||
RecentCharacter("2", "Yubin...", ""),
|
|
||||||
RecentCharacter("3", "Yubin...", ""),
|
|
||||||
RecentCharacter("4", "Yubin...", ""),
|
|
||||||
RecentCharacter("5", "Yubin...", "")
|
|
||||||
)
|
|
||||||
|
|
||||||
recentCharacterAdapter.updateCharacters(recentCharacters)
|
|
||||||
binding.tvLatestCharacterCount.text = recentCharacters.size.toString()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun loadPopularCharacters() {
|
|
||||||
// TODO: 서버에서 인기 캐릭터 데이터 로드
|
|
||||||
val popularCharacters = listOf(
|
|
||||||
Character("1", "캐릭터 이름", "캐릭터 한줄 소개인데 2줄까", ""),
|
|
||||||
Character("2", "캐릭터 이름", "캐릭터 한줄 소개인데 2줄까", ""),
|
|
||||||
Character("3", "캐릭터 이름", "#태그#태그#태그", ""),
|
|
||||||
Character("4", "캐릭터 이름", "캐릭터 한줄 소개인데 2줄까", ""),
|
|
||||||
Character("5", "캐릭터 이름", "캐릭터 한줄 소개인데 2줄까", "")
|
|
||||||
)
|
|
||||||
|
|
||||||
popularCharacterAdapter.updateCharacters(popularCharacters)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun loadNewCharacters() {
|
|
||||||
// TODO: 서버에서 신규 캐릭터 데이터 로드
|
|
||||||
val newCharacters = listOf(
|
|
||||||
Character("1", "캐릭터 이름", "캐릭터 한줄 소개인데 2줄까", ""),
|
|
||||||
Character("2", "하이퍼나이프", "캐릭터 한줄 소개인데 2줄까", ""),
|
|
||||||
Character("3", "내일", "#태그#태그", "")
|
|
||||||
)
|
|
||||||
|
|
||||||
newCharacterAdapter.updateCharacters(newCharacters)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun loadCurationSections() {
|
|
||||||
// TODO: 서버에서 큐레이션 섹션 데이터 로드
|
|
||||||
val curationSections = listOf(
|
|
||||||
CurationSection(
|
|
||||||
"1",
|
|
||||||
"큐레이션",
|
|
||||||
listOf(
|
|
||||||
Character("1", "캐릭터 이름", "캐릭터 한줄 소개인데 2줄까", ""),
|
|
||||||
Character("2", "하이퍼나이프", "캐릭터 한줄 소개인데 2줄까", ""),
|
|
||||||
Character("3", "내일", "#태그#태그", "")
|
|
||||||
)
|
|
||||||
),
|
|
||||||
CurationSection(
|
|
||||||
"2",
|
|
||||||
"큐레이션",
|
|
||||||
listOf(
|
|
||||||
Character("4", "캐릭터 이름", "캐릭터 한줄 소개인데 2줄까", ""),
|
|
||||||
Character("5", "하이퍼나이프", "캐릭터 한줄 소개인데 2줄까", ""),
|
|
||||||
Character("6", "내일", "#태그#태그", "")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
curationSectionAdapter.updateSections(curationSections)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onRecentCharacterClick(character: RecentCharacter) {
|
private fun onRecentCharacterClick(character: RecentCharacter) {
|
||||||
// TODO: 최근 대화한 캐릭터 클릭 처리
|
// TODO: 최근 대화한 캐릭터 클릭 처리
|
||||||
|
|||||||
@@ -5,7 +5,10 @@ import androidx.lifecycle.MutableLiveData
|
|||||||
import com.orhanobut.logger.Logger
|
import com.orhanobut.logger.Logger
|
||||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||||
import io.reactivex.rxjava3.schedulers.Schedulers
|
import io.reactivex.rxjava3.schedulers.Schedulers
|
||||||
|
import kr.co.vividnext.sodalive.audio_content.main.GetAudioContentBannerResponse
|
||||||
import kr.co.vividnext.sodalive.base.BaseViewModel
|
import kr.co.vividnext.sodalive.base.BaseViewModel
|
||||||
|
import kr.co.vividnext.sodalive.chat.character.curation.CurationSection
|
||||||
|
import kr.co.vividnext.sodalive.chat.character.recent.RecentCharacter
|
||||||
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
||||||
|
|
||||||
class CharacterTabViewModel(
|
class CharacterTabViewModel(
|
||||||
@@ -19,6 +22,30 @@ class CharacterTabViewModel(
|
|||||||
val toastLiveData: LiveData<String?>
|
val toastLiveData: LiveData<String?>
|
||||||
get() = _toastLiveData
|
get() = _toastLiveData
|
||||||
|
|
||||||
|
private var _bannerListLiveData = MutableLiveData<List<GetAudioContentBannerResponse>>()
|
||||||
|
val bannerListLiveData: LiveData<List<GetAudioContentBannerResponse>>
|
||||||
|
get() = _bannerListLiveData
|
||||||
|
|
||||||
|
// 최근 대화한 캐릭터 LiveData
|
||||||
|
private val _recentCharacters = MutableLiveData<List<RecentCharacter>>(emptyList())
|
||||||
|
val recentCharacters: LiveData<List<RecentCharacter>>
|
||||||
|
get() = _recentCharacters
|
||||||
|
|
||||||
|
// 인기 캐릭터 LiveData
|
||||||
|
private val _popularCharacters = MutableLiveData<List<Character>>(emptyList())
|
||||||
|
val popularCharacters: LiveData<List<Character>>
|
||||||
|
get() = _popularCharacters
|
||||||
|
|
||||||
|
// 신규 캐릭터 LiveData
|
||||||
|
private val _newCharacters = MutableLiveData<List<Character>>(emptyList())
|
||||||
|
val newCharacters: LiveData<List<Character>>
|
||||||
|
get() = _newCharacters
|
||||||
|
|
||||||
|
// 큐레이션 섹션 LiveData
|
||||||
|
private val _curationSections = MutableLiveData<List<CurationSection>>(emptyList())
|
||||||
|
val curationSections: LiveData<List<CurationSection>>
|
||||||
|
get() = _curationSections
|
||||||
|
|
||||||
fun fetchData() {
|
fun fetchData() {
|
||||||
_isLoading.value = true
|
_isLoading.value = true
|
||||||
|
|
||||||
@@ -31,7 +58,11 @@ class CharacterTabViewModel(
|
|||||||
_isLoading.value = false
|
_isLoading.value = false
|
||||||
val data = it.data
|
val data = it.data
|
||||||
if (it.success && data != null) {
|
if (it.success && data != null) {
|
||||||
|
_bannerListLiveData.value = data.banners
|
||||||
|
_recentCharacters.value = data.recentCharacters
|
||||||
|
_popularCharacters.value = data.popularCharacters
|
||||||
|
_newCharacters.value = data.newCharacters
|
||||||
|
_curationSections.value = data.curationSections
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -37,6 +37,7 @@
|
|||||||
|
|
||||||
<!-- 최근 대화한 캐릭터 섹션 -->
|
<!-- 최근 대화한 캐릭터 섹션 -->
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
|
android:id="@+id/ll_latest_characters"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginBottom="48dp"
|
android:layout_marginBottom="48dp"
|
||||||
@@ -83,6 +84,7 @@
|
|||||||
|
|
||||||
<!-- 인기 캐릭터 섹션 -->
|
<!-- 인기 캐릭터 섹션 -->
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
|
android:id="@+id/ll_popular_characters"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginBottom="48dp"
|
android:layout_marginBottom="48dp"
|
||||||
@@ -127,6 +129,7 @@
|
|||||||
|
|
||||||
<!-- 신규 캐릭터 섹션 -->
|
<!-- 신규 캐릭터 섹션 -->
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
|
android:id="@+id/ll_new_characters"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginBottom="48dp"
|
android:layout_marginBottom="48dp"
|
||||||
|
|||||||
Reference in New Issue
Block a user