feat(home): AI 캐릭터 크리에이터 이동을 연결한다
This commit is contained in:
@@ -7,7 +7,6 @@ import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import kr.co.vividnext.sodalive.R
|
||||
import kr.co.vividnext.sodalive.audio_content.detail.AudioContentDetailActivity
|
||||
import kr.co.vividnext.sodalive.base.BaseFragment
|
||||
import kr.co.vividnext.sodalive.chat.character.detail.CharacterDetailActivity
|
||||
import kr.co.vividnext.sodalive.common.Constants
|
||||
import kr.co.vividnext.sodalive.common.LoadingDialog
|
||||
import kr.co.vividnext.sodalive.common.ToastMessage
|
||||
@@ -18,6 +17,7 @@ import kr.co.vividnext.sodalive.explorer.profile.creator_community.all.CreatorCo
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivity
|
||||
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.HomeRecommendationAiCharacterUiModel
|
||||
import kr.co.vividnext.sodalive.v2.main.home.model.HomeRecommendationBannerSection
|
||||
import kr.co.vividnext.sodalive.v2.main.home.model.HomeRecommendationBannerUiModel
|
||||
import kr.co.vividnext.sodalive.v2.main.home.model.HomeRecommendationCheerCreatorSection
|
||||
@@ -35,6 +35,8 @@ import kr.co.vividnext.sodalive.v2.main.home.model.HomeRecommendationRecentlyAct
|
||||
import kr.co.vividnext.sodalive.v2.main.home.model.HomeRecommendationUiState
|
||||
import kr.co.vividnext.sodalive.v2.main.home.model.toHomeRecommendationBannerIntent
|
||||
import kr.co.vividnext.sodalive.v2.main.home.model.toHomeRecommendationBannerRoute
|
||||
import kr.co.vividnext.sodalive.v2.main.home.model.toHomeRecommendationAiCharacterIntent
|
||||
import kr.co.vividnext.sodalive.v2.main.home.model.toHomeRecommendationAiCharacterRoute
|
||||
import kr.co.vividnext.sodalive.v2.main.home.model.toHomeRecommendationRecentlyActiveCreatorIntent
|
||||
import kr.co.vividnext.sodalive.v2.main.home.model.toHomeRecommendationRecentlyActiveCreatorRoute
|
||||
import kr.co.vividnext.sodalive.v2.main.home.model.visibleHomeGenreCreatorGroups
|
||||
@@ -63,7 +65,7 @@ class HomeMainFragment : BaseFragment<FragmentV2MainHomeBinding>(
|
||||
private val recentActivityCreatorAdapter = HomeRecentActivityCreatorAdapter { onRecentActivityClick(it) }
|
||||
private val recentDebutCreatorAdapter = HomeRecentDebutCreatorAdapter { openCreatorProfile(it.creatorId) }
|
||||
private val firstAudioAdapter = HomeFirstAudioAdapter { openAudioContentDetail(it) }
|
||||
private val aiCharacterAdapter = HomeAiCharacterAdapter { openCharacterDetail(it.characterId) }
|
||||
private val aiCharacterAdapter = HomeAiCharacterAdapter { onAiCharacterClick(it) }
|
||||
private val genreCreatorAdapter = HomeGenreCreatorAdapter(
|
||||
onFollowAllClick = { creatorIds -> onGenreFollowAllClick(creatorIds) },
|
||||
onCreatorClick = { creator -> openCreatorProfile(creator.creatorId) }
|
||||
@@ -339,6 +341,11 @@ class HomeMainFragment : BaseFragment<FragmentV2MainHomeBinding>(
|
||||
startActivity(route.toHomeRecommendationRecentlyActiveCreatorIntent(requireContext()))
|
||||
}
|
||||
|
||||
private fun onAiCharacterClick(item: HomeRecommendationAiCharacterUiModel) {
|
||||
val route = item.toHomeRecommendationAiCharacterRoute() ?: return
|
||||
startActivity(route.toHomeRecommendationAiCharacterIntent(requireContext()))
|
||||
}
|
||||
|
||||
private fun openCreatorRankingProfile(item: CreatorRankingItem) {
|
||||
if (item.creatorId <= 0L) return
|
||||
openCreatorProfile(item.creatorId)
|
||||
@@ -358,14 +365,6 @@ class HomeMainFragment : BaseFragment<FragmentV2MainHomeBinding>(
|
||||
)
|
||||
}
|
||||
|
||||
private fun openCharacterDetail(characterId: Long) {
|
||||
startActivity(
|
||||
Intent(requireContext(), CharacterDetailActivity::class.java).apply {
|
||||
putExtra(CharacterDetailActivity.EXTRA_CHARACTER_ID, characterId)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private fun openPopularCommunityPost(item: FeedItem.Community) {
|
||||
val creatorId = item.creatorId.toLongOrNull() ?: return
|
||||
val postId = item.postId.toLongOrNull() ?: return
|
||||
|
||||
@@ -64,6 +64,7 @@ data class HomeFirstAudioContentItem(
|
||||
@Keep
|
||||
data class HomeAiCharacterItem(
|
||||
@SerializedName("characterId") val characterId: Long,
|
||||
@SerializedName("creatorId") val creatorId: Long,
|
||||
@SerializedName("name") val name: String,
|
||||
@SerializedName("description") val description: String,
|
||||
@SerializedName("profileImage") val profileImage: String?,
|
||||
|
||||
@@ -79,6 +79,7 @@ fun HomeFirstAudioContentItem.toUiModel(): HomeRecommendationFirstAudioContentUi
|
||||
)
|
||||
|
||||
fun HomeAiCharacterItem.toUiModel(): HomeRecommendationAiCharacterUiModel = HomeRecommendationAiCharacterUiModel(
|
||||
creatorId = creatorId,
|
||||
item = CharacterChatThumbnailItem(
|
||||
characterId = characterId,
|
||||
imageUrl = profileImage.orEmpty(),
|
||||
|
||||
@@ -191,9 +191,24 @@ data class HomeRecommendationFirstAudioContentUiModel(
|
||||
)
|
||||
|
||||
data class HomeRecommendationAiCharacterUiModel(
|
||||
val creatorId: Long,
|
||||
val item: CharacterChatThumbnailItem
|
||||
)
|
||||
|
||||
sealed interface HomeRecommendationAiCharacterRoute {
|
||||
data class Creator(val creatorId: Long) : HomeRecommendationAiCharacterRoute
|
||||
}
|
||||
|
||||
fun HomeRecommendationAiCharacterUiModel.toHomeRecommendationAiCharacterRoute(): HomeRecommendationAiCharacterRoute? {
|
||||
return creatorId.takeIf { it > 0L }?.let(HomeRecommendationAiCharacterRoute::Creator)
|
||||
}
|
||||
|
||||
fun HomeRecommendationAiCharacterRoute.toHomeRecommendationAiCharacterIntent(context: Context): Intent {
|
||||
return when (this) {
|
||||
is HomeRecommendationAiCharacterRoute.Creator -> CreatorChannelActivity.newIntent(context, creatorId)
|
||||
}
|
||||
}
|
||||
|
||||
data class HomeRecommendationGenreCreatorGroupUiModel(
|
||||
val genre: String,
|
||||
val creators: List<HomeRecommendationCreatorUiModel>,
|
||||
|
||||
@@ -6,11 +6,10 @@ import androidx.recyclerview.widget.RecyclerView
|
||||
import kr.co.vividnext.sodalive.R
|
||||
import kr.co.vividnext.sodalive.extensions.loadUrl
|
||||
import kr.co.vividnext.sodalive.v2.main.home.model.HomeRecommendationAiCharacterUiModel
|
||||
import kr.co.vividnext.sodalive.v2.widget.characterchatthumbnail.CharacterChatThumbnailItem
|
||||
import kr.co.vividnext.sodalive.v2.widget.characterchatthumbnail.CharacterChatThumbnailView
|
||||
|
||||
class HomeAiCharacterAdapter(
|
||||
private val onClickItem: (CharacterChatThumbnailItem) -> Unit = {}
|
||||
private val onClickItem: (HomeRecommendationAiCharacterUiModel) -> Unit = {}
|
||||
) : RecyclerView.Adapter<HomeAiCharacterAdapter.CharacterViewHolder>() {
|
||||
private var items: List<HomeRecommendationAiCharacterUiModel> = emptyList()
|
||||
|
||||
@@ -31,19 +30,19 @@ class HomeAiCharacterAdapter(
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: CharacterViewHolder, position: Int) {
|
||||
holder.bind(items[position].item)
|
||||
holder.bind(items[position])
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = items.size
|
||||
|
||||
class CharacterViewHolder(
|
||||
private val view: CharacterChatThumbnailView,
|
||||
private val onClickItem: (CharacterChatThumbnailItem) -> Unit
|
||||
private val onClickItem: (HomeRecommendationAiCharacterUiModel) -> Unit
|
||||
) : RecyclerView.ViewHolder(view) {
|
||||
fun bind(item: CharacterChatThumbnailItem) {
|
||||
view.bind(item)
|
||||
view.imageView().loadUrl(item.imageUrl)
|
||||
view.setOnCharacterClick(onClickItem)
|
||||
fun bind(item: HomeRecommendationAiCharacterUiModel) {
|
||||
view.bind(item.item)
|
||||
view.imageView().loadUrl(item.item.imageUrl)
|
||||
view.setOnCharacterClick { onClickItem(item) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ import kr.co.vividnext.sodalive.settings.event.EventDetailActivity
|
||||
import kr.co.vividnext.sodalive.settings.event.EventItem
|
||||
import kr.co.vividnext.sodalive.v2.creator.channel.CreatorChannelActivity
|
||||
import kr.co.vividnext.sodalive.v2.main.home.data.HomeActiveCreatorItem
|
||||
import kr.co.vividnext.sodalive.v2.main.home.data.HomeAiCharacterItem
|
||||
import kr.co.vividnext.sodalive.v2.main.home.data.HomeBannerItem
|
||||
import kr.co.vividnext.sodalive.v2.main.home.data.HomeCreatorItem
|
||||
import kr.co.vividnext.sodalive.v2.main.home.data.HomeFirstAudioContentItem
|
||||
@@ -40,6 +41,7 @@ import kr.co.vividnext.sodalive.v2.main.home.data.HomeLiveItem
|
||||
import kr.co.vividnext.sodalive.v2.main.home.model.HomeRecommendationBannerSection
|
||||
import kr.co.vividnext.sodalive.v2.main.home.model.HomeRecommendationBannerRoute
|
||||
import kr.co.vividnext.sodalive.v2.main.home.model.HomeRecommendationBannerUiModel
|
||||
import kr.co.vividnext.sodalive.v2.main.home.model.HomeRecommendationAiCharacterRoute
|
||||
import kr.co.vividnext.sodalive.v2.main.home.model.HomeRecommendationFirstAudioContentUiModel
|
||||
import kr.co.vividnext.sodalive.v2.main.home.model.HomeRecommendationCreatorUiModel
|
||||
import kr.co.vividnext.sodalive.v2.main.home.model.HomeRecommendationGenreCreatorGroupUiModel
|
||||
@@ -53,6 +55,8 @@ import kr.co.vividnext.sodalive.v2.main.home.model.HomeRecommendationRecentlyAct
|
||||
import kr.co.vividnext.sodalive.v2.common.CreatorActivityType
|
||||
import kr.co.vividnext.sodalive.v2.main.home.model.toHomeRecommendationBannerIntent
|
||||
import kr.co.vividnext.sodalive.v2.main.home.model.toHomeRecommendationBannerRoute
|
||||
import kr.co.vividnext.sodalive.v2.main.home.model.toHomeRecommendationAiCharacterIntent
|
||||
import kr.co.vividnext.sodalive.v2.main.home.model.toHomeRecommendationAiCharacterRoute
|
||||
import kr.co.vividnext.sodalive.v2.main.home.model.toHomeRecommendationRecentlyActiveCreatorIntent
|
||||
import kr.co.vividnext.sodalive.v2.main.home.model.toHomeRecommendationRecentlyActiveCreatorRoute
|
||||
import kr.co.vividnext.sodalive.v2.main.home.model.visibleHomePopularCommunityPosts
|
||||
@@ -1087,6 +1091,41 @@ class HomeMainFragmentLayoutTest {
|
||||
assertEquals(13L, communityIntent.getLongExtra(Constants.EXTRA_COMMUNITY_CREATOR_ID, 0L))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `home ai character mapper preserves creator id for routing and character id for display`() {
|
||||
val item = HomeAiCharacterItem(
|
||||
characterId = 11L,
|
||||
creatorId = 22L,
|
||||
name = "캐릭터",
|
||||
description = "설명",
|
||||
profileImage = "https://example.com/character.png",
|
||||
totalChatCount = 33L,
|
||||
originalWorkTitle = "원작"
|
||||
).toUiModel()
|
||||
|
||||
assertEquals(22L, item.creatorId)
|
||||
assertEquals(11L, item.item.characterId)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `home ai character route uses creator id and ignores invalid ids`() {
|
||||
assertEquals(
|
||||
HomeRecommendationAiCharacterRoute.Creator(22L),
|
||||
aiCharacter(creatorId = 22L).toHomeRecommendationAiCharacterRoute()
|
||||
)
|
||||
assertEquals(null, aiCharacter(creatorId = 0L).toHomeRecommendationAiCharacterRoute())
|
||||
assertEquals(null, aiCharacter(creatorId = -1L).toHomeRecommendationAiCharacterRoute())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `home ai character route creates creator channel intent`() {
|
||||
val context = ApplicationProvider.getApplicationContext<Context>()
|
||||
val intent = HomeRecommendationAiCharacterRoute.Creator(22L).toHomeRecommendationAiCharacterIntent(context)
|
||||
|
||||
assertEquals(CreatorChannelActivity::class.java.name, intent.component?.className)
|
||||
assertEquals(22L, intent.getLongExtra(CreatorChannelActivity.EXTRA_CREATOR_ID, 0L))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `home popular community adapter applies blur when locked paid post image is loaded`() {
|
||||
val source = projectFile(
|
||||
@@ -1517,6 +1556,18 @@ class HomeMainFragmentLayoutTest {
|
||||
)
|
||||
}
|
||||
|
||||
private fun aiCharacter(creatorId: Long): kr.co.vividnext.sodalive.v2.main.home.model.HomeRecommendationAiCharacterUiModel {
|
||||
return HomeAiCharacterItem(
|
||||
characterId = 11L,
|
||||
creatorId = creatorId,
|
||||
name = "캐릭터",
|
||||
description = "설명",
|
||||
profileImage = null,
|
||||
totalChatCount = 33L,
|
||||
originalWorkTitle = null
|
||||
).toUiModel()
|
||||
}
|
||||
|
||||
private fun popularCommunityData(
|
||||
audioUrl: String?,
|
||||
createdAt: String = "2분 전"
|
||||
|
||||
Reference in New Issue
Block a user