feat(home): 홈 추천 UI 상태 모델을 추가한다

This commit is contained in:
2026-06-02 13:28:32 +09:00
parent 4743c9cb8f
commit dd002e9f82
3 changed files with 265 additions and 0 deletions

View File

@@ -1,6 +1,124 @@
package kr.co.vividnext.sodalive.v2.main.home.model package kr.co.vividnext.sodalive.v2.main.home.model
import androidx.annotation.StringRes import androidx.annotation.StringRes
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
import kr.co.vividnext.sodalive.v2.main.home.data.HomeGenreCreatorGroupItem
import kr.co.vividnext.sodalive.v2.main.home.data.HomeLiveItem
import kr.co.vividnext.sodalive.v2.main.home.data.HomePopularCommunityPostItem
import kr.co.vividnext.sodalive.v2.main.home.data.HomeRecommendationResponse
import kr.co.vividnext.sodalive.v2.widget.AudioContentTag
import kr.co.vividnext.sodalive.v2.widget.characterchatthumbnail.CharacterChatThumbnailItem
import kr.co.vividnext.sodalive.v2.widget.feed.FeedItem
@StringRes @StringRes
fun String.toRecommendedActivityTypeLabelRes(): Int? = RecommendedActivityType.from(this)?.labelResId fun String.toRecommendedActivityTypeLabelRes(): Int? = RecommendedActivityType.from(this)?.labelResId
fun HomeRecommendationResponse.toContent(): HomeRecommendationUiState.Content = HomeRecommendationUiState.Content(
lives = HomeRecommendationLiveSection(lives.map { it.toUiModel() }),
banners = HomeRecommendationBannerSection(banners.map { it.toUiModel() }),
recentlyActiveCreators = HomeRecommendationRecentlyActiveCreatorSection(recentlyActiveCreators.map { it.toUiModel() }),
recentDebutCreators = HomeRecommendationRecentDebutCreatorSection(recentDebutCreators.map { it.toUiModel() }),
firstAudioContents = HomeRecommendationFirstAudioContentSection(firstAudioContents.map { it.toUiModel() }),
aiCharacters = HomeRecommendationAiCharacterSection(aiCharacters.map { it.toUiModel() }),
genreCreators = HomeRecommendationGenreCreatorSection(genreCreators.map { it.toUiModel() }),
cheerCreators = HomeRecommendationCheerCreatorSection(cheerCreators.map { it.toUiModel() }),
popularCommunityPosts = HomeRecommendationPopularCommunityPostSection(popularCommunityPosts.map { it.toUiModel() })
)
fun HomeLiveItem.toUiModel(): HomeRecommendationLiveUiModel = HomeRecommendationLiveUiModel(
liveId = liveId,
creatorId = creatorId,
imageUrl = imageUrl,
title = title,
creatorNickname = creatorNickname,
beginDateTime = beginDateTime
)
fun HomeBannerItem.toUiModel(): HomeRecommendationBannerUiModel = HomeRecommendationBannerUiModel(
bannerId = bannerId,
imageUrl = imageUrl,
type = type,
linkUrl = linkUrl,
targetId = targetId
)
fun HomeActiveCreatorItem.toUiModel(): HomeRecommendationRecentlyActiveCreatorUiModel {
val recommendedActivityType = RecommendedActivityType.from(activityType)
return HomeRecommendationRecentlyActiveCreatorUiModel(
creatorId = creatorId,
nickname = nickname,
profileImage = profileImage,
title = title,
activityType = recommendedActivityType,
activityLabelResId = recommendedActivityType?.labelResId,
activityAt = activityAt
)
}
fun HomeCreatorItem.toUiModel(): HomeRecommendationCreatorUiModel = HomeRecommendationCreatorUiModel(
creatorId = creatorId,
nickname = nickname,
profileImage = profileImage
)
fun HomeFirstAudioContentItem.toUiModel(): HomeRecommendationFirstAudioContentUiModel =
HomeRecommendationFirstAudioContentUiModel(
contentId = contentId,
creatorId = creatorId,
creatorNickname = creatorNickname,
creatorProfileImage = creatorProfileImage,
title = title,
price = price,
coverImage = coverImage,
releaseDate = releaseDate,
tags = buildSet {
add(AudioContentTag.First)
if (isPointAvailable) add(AudioContentTag.Point)
if (price == 0) add(AudioContentTag.Free)
}
)
fun HomeAiCharacterItem.toUiModel(): HomeRecommendationAiCharacterUiModel = HomeRecommendationAiCharacterUiModel(
item = CharacterChatThumbnailItem(
characterId = characterId,
imageUrl = profileImage.orEmpty(),
characterName = name,
characterDescription = description,
chatMessageCount = totalChatCount,
hasOriginal = originalWorkTitle != null,
originalTitle = originalWorkTitle.orEmpty()
)
)
fun HomeGenreCreatorGroupItem.toUiModel(): HomeRecommendationGenreCreatorGroupUiModel =
HomeRecommendationGenreCreatorGroupUiModel(
genre = genre,
creators = creators.map { it.toUiModel() }
)
fun HomePopularCommunityPostItem.toUiModel(): HomeRecommendationPopularCommunityPostUiModel =
HomeRecommendationPopularCommunityPostUiModel(
item = FeedItem.Community(
feedId = postId.toString(),
creatorId = creatorId.toString(),
creatorName = creatorNickname,
creatorImageUrl = creatorProfileImage.orEmpty(),
postId = postId.toString(),
bodyText = content,
keywordText = "",
createdAtText = createdAt,
commentCount = commentCount.toInt(),
likeCount = likeCount.toInt()
),
paidStatus = toPaidStatus()
)
fun HomePopularCommunityPostItem.toPaidStatus(): HomeRecommendationPaidStatus = when {
price == 0 -> HomeRecommendationPaidStatus.Free
existOrdered -> HomeRecommendationPaidStatus.Purchased
else -> HomeRecommendationPaidStatus.Paid(price)
}

View File

@@ -0,0 +1,112 @@
package kr.co.vividnext.sodalive.v2.main.home.model
import kr.co.vividnext.sodalive.v2.widget.AudioContentTag
import kr.co.vividnext.sodalive.v2.widget.characterchatthumbnail.CharacterChatThumbnailItem
import kr.co.vividnext.sodalive.v2.widget.feed.FeedItem
data class HomeRecommendationLiveSection(
val items: List<HomeRecommendationLiveUiModel>
)
data class HomeRecommendationBannerSection(
val items: List<HomeRecommendationBannerUiModel>
)
data class HomeRecommendationRecentlyActiveCreatorSection(
val items: List<HomeRecommendationRecentlyActiveCreatorUiModel>
)
data class HomeRecommendationRecentDebutCreatorSection(
val items: List<HomeRecommendationCreatorUiModel>
)
data class HomeRecommendationFirstAudioContentSection(
val items: List<HomeRecommendationFirstAudioContentUiModel>
)
data class HomeRecommendationAiCharacterSection(
val items: List<HomeRecommendationAiCharacterUiModel>
)
data class HomeRecommendationGenreCreatorSection(
val groups: List<HomeRecommendationGenreCreatorGroupUiModel>
)
data class HomeRecommendationCheerCreatorSection(
val items: List<HomeRecommendationCreatorUiModel>,
val isFollowCompleted: Boolean = false
)
data class HomeRecommendationPopularCommunityPostSection(
val items: List<HomeRecommendationPopularCommunityPostUiModel>
)
data class HomeRecommendationLiveUiModel(
val liveId: Long,
val creatorId: Long,
val imageUrl: String?,
val title: String,
val creatorNickname: String,
val beginDateTime: String?
)
data class HomeRecommendationBannerUiModel(
val bannerId: String?,
val imageUrl: String?,
val type: String?,
val linkUrl: String?,
val targetId: Long?
)
data class HomeRecommendationRecentlyActiveCreatorUiModel(
val creatorId: Long,
val nickname: String,
val profileImage: String?,
val title: String?,
val activityType: RecommendedActivityType?,
val activityLabelResId: Int?,
val activityAt: String?
)
data class HomeRecommendationCreatorUiModel(
val creatorId: Long,
val nickname: String,
val profileImage: String?
)
data class HomeRecommendationFirstAudioContentUiModel(
val contentId: Long,
val creatorId: Long,
val creatorNickname: String,
val creatorProfileImage: String?,
val title: String,
val price: Int,
val coverImage: String?,
val releaseDate: String,
val tags: Set<AudioContentTag>
)
data class HomeRecommendationAiCharacterUiModel(
val item: CharacterChatThumbnailItem
)
data class HomeRecommendationGenreCreatorGroupUiModel(
val genre: String,
val creators: List<HomeRecommendationCreatorUiModel>,
val isFollowCompleted: Boolean = false
)
data class HomeRecommendationPopularCommunityPostUiModel(
val item: FeedItem.Community,
val paidStatus: HomeRecommendationPaidStatus
)
sealed interface HomeRecommendationPaidStatus {
data object Free : HomeRecommendationPaidStatus
data object Purchased : HomeRecommendationPaidStatus
data class Paid(
val price: Int
) : HomeRecommendationPaidStatus
}

View File

@@ -0,0 +1,35 @@
package kr.co.vividnext.sodalive.v2.main.home.model
sealed interface HomeRecommendationUiState {
data object Loading : HomeRecommendationUiState
data class Content(
val lives: HomeRecommendationLiveSection,
val banners: HomeRecommendationBannerSection,
val recentlyActiveCreators: HomeRecommendationRecentlyActiveCreatorSection,
val recentDebutCreators: HomeRecommendationRecentDebutCreatorSection,
val firstAudioContents: HomeRecommendationFirstAudioContentSection,
val aiCharacters: HomeRecommendationAiCharacterSection,
val genreCreators: HomeRecommendationGenreCreatorSection,
val cheerCreators: HomeRecommendationCheerCreatorSection,
val popularCommunityPosts: HomeRecommendationPopularCommunityPostSection
) : HomeRecommendationUiState {
val isEmpty: Boolean = listOf(
lives.items,
banners.items,
recentlyActiveCreators.items,
recentDebutCreators.items,
firstAudioContents.items,
aiCharacters.items,
genreCreators.groups,
cheerCreators.items,
popularCommunityPosts.items
).all { it.isEmpty() }
}
data object Empty : HomeRecommendationUiState
data class Error(
val message: String? = null
) : HomeRecommendationUiState
}