feat(home): 최근 활동 LIVE 라우팅을 추가한다

This commit is contained in:
2026-07-30 16:42:33 +09:00
parent a1aa9c2243
commit f600d10e8b
10 changed files with 366 additions and 8 deletions

View File

@@ -707,6 +707,10 @@ class HomeMainFragment : BaseFragment<FragmentV2MainHomeBinding>(
private fun onRecentActivityClick(item: HomeRecommendationRecentlyActiveCreatorUiModel) {
val route = item.toHomeRecommendationRecentlyActiveCreatorRoute() ?: return
when (route) {
is HomeRecommendationRecentlyActiveCreatorRoute.LiveRoom -> liveActionCoordinator.enterLiveRoom(route.roomId)
is HomeRecommendationRecentlyActiveCreatorRoute.CreatorChannel -> handleCreatorAction(
CreatorActionCommand.Profile(route.creatorId)
)
is HomeRecommendationRecentlyActiveCreatorRoute.AudioContent -> handleContentAction(
ContentActionCommand.AudioDetail(audioContentId = route.contentId)
)

View File

@@ -34,6 +34,7 @@ data class HomeBannerItem(
@Keep
data class HomeActiveCreatorItem(
@SerializedName("creatorId") val creatorId: Long,
@SerializedName("creatorNickname") val creatorNickname: String,
@SerializedName("creatorProfileImage") val creatorProfileImage: String,
@SerializedName("activityType") val activityType: String,

View File

@@ -44,6 +44,7 @@ fun HomeBannerItem.toUiModel(): HomeRecommendationBannerUiModel = HomeRecommenda
fun HomeActiveCreatorItem.toUiModel(): HomeRecommendationRecentlyActiveCreatorUiModel {
val creatorActivityType = CreatorActivityType.from(activityType)
return HomeRecommendationRecentlyActiveCreatorUiModel(
creatorId = creatorId,
nickname = creatorNickname,
profileImage = creatorProfileImage,
activityType = creatorActivityType,

View File

@@ -116,6 +116,7 @@ fun HomeRecommendationBannerRoute.toHomeRecommendationBannerIntent(context: Cont
private fun homeRecommendationAppLinkHost(): String = "${BuildConfig.APPSCHEME}.onelink.me"
data class HomeRecommendationRecentlyActiveCreatorUiModel(
val creatorId: Long,
val nickname: String,
val profileImage: String,
val activityType: CreatorActivityType?,
@@ -125,6 +126,10 @@ data class HomeRecommendationRecentlyActiveCreatorUiModel(
)
sealed interface HomeRecommendationRecentlyActiveCreatorRoute {
data class LiveRoom(val roomId: Long) : HomeRecommendationRecentlyActiveCreatorRoute
data class CreatorChannel(val creatorId: Long) : HomeRecommendationRecentlyActiveCreatorRoute
data class AudioContent(val contentId: Long) : HomeRecommendationRecentlyActiveCreatorRoute
data class Community(val postId: Long) : HomeRecommendationRecentlyActiveCreatorRoute
@@ -132,12 +137,18 @@ sealed interface HomeRecommendationRecentlyActiveCreatorRoute {
fun HomeRecommendationRecentlyActiveCreatorUiModel.toHomeRecommendationRecentlyActiveCreatorRoute():
HomeRecommendationRecentlyActiveCreatorRoute? {
val routeTargetId = targetId?.takeIf { it > 0 } ?: return null
return when (activityType) {
CreatorActivityType.Live -> null
CreatorActivityType.Live -> when {
targetId == null -> creatorId.takeIf { it > 0L }
?.let(HomeRecommendationRecentlyActiveCreatorRoute::CreatorChannel)
targetId > 0L -> HomeRecommendationRecentlyActiveCreatorRoute.LiveRoom(targetId)
else -> null
}
CreatorActivityType.LiveReplay,
CreatorActivityType.Audio -> HomeRecommendationRecentlyActiveCreatorRoute.AudioContent(routeTargetId)
CreatorActivityType.Community -> HomeRecommendationRecentlyActiveCreatorRoute.Community(routeTargetId)
CreatorActivityType.Audio -> targetId?.takeIf { it > 0L }
?.let(HomeRecommendationRecentlyActiveCreatorRoute::AudioContent)
CreatorActivityType.Community -> targetId?.takeIf { it > 0L }
?.let(HomeRecommendationRecentlyActiveCreatorRoute::Community)
null -> null
}
}

View File

@@ -720,6 +720,7 @@ class HomeMainFragmentLayoutTest {
link = "https://example.com"
).toUiModel()
val activeCreator = HomeActiveCreatorItem(
creatorId = 45L,
creatorNickname = "활동 크리에이터",
creatorProfileImage = "https://example.com/active.png",
activityType = "AUDIO",
@@ -749,6 +750,7 @@ class HomeMainFragmentLayoutTest {
assertEquals(30L, banner.creatorId)
assertEquals(40L, banner.seriesId)
assertEquals("https://example.com", banner.link)
assertEquals(45L, activeCreator.creatorId)
assertEquals("활동 크리에이터", activeCreator.nickname)
assertEquals("https://example.com/active.png", activeCreator.profileImage)
assertEquals("방금 전", activeCreator.activityAt)
@@ -904,6 +906,11 @@ class HomeMainFragmentLayoutTest {
@Test
fun `home recent activity route maps activity type to target destination`() {
assertEquals(
HomeRecommendationRecentlyActiveCreatorRoute.LiveRoom(10L),
recentActivityCreator(CreatorActivityType.Live, targetId = 10L)
.toHomeRecommendationRecentlyActiveCreatorRoute()
)
assertEquals(
HomeRecommendationRecentlyActiveCreatorRoute.AudioContent(11L),
recentActivityCreator(CreatorActivityType.LiveReplay, targetId = 11L)
@@ -922,12 +929,41 @@ class HomeMainFragmentLayoutTest {
}
@Test
fun `home recent activity route ignores live unknown and missing target`() {
fun `홈 최근 활동 LIVE는 target 유무에 따라 라이브 룸 또는 크리에이터 채널로 이동한다`() {
assertEquals(
null,
recentActivityCreator(CreatorActivityType.Live, targetId = 10L)
HomeRecommendationRecentlyActiveCreatorRoute.LiveRoom(10L),
recentActivityCreator(CreatorActivityType.Live, targetId = 10L, creatorId = 20L)
.toHomeRecommendationRecentlyActiveCreatorRoute()
)
assertEquals(
HomeRecommendationRecentlyActiveCreatorRoute.CreatorChannel(20L),
recentActivityCreator(CreatorActivityType.Live, targetId = null, creatorId = 20L)
.toHomeRecommendationRecentlyActiveCreatorRoute()
)
assertEquals(
null,
recentActivityCreator(CreatorActivityType.Live, targetId = 0L, creatorId = 20L)
.toHomeRecommendationRecentlyActiveCreatorRoute()
)
assertEquals(
null,
recentActivityCreator(CreatorActivityType.Live, targetId = -1L, creatorId = 20L)
.toHomeRecommendationRecentlyActiveCreatorRoute()
)
assertEquals(
null,
recentActivityCreator(CreatorActivityType.Live, targetId = null, creatorId = 0L)
.toHomeRecommendationRecentlyActiveCreatorRoute()
)
assertEquals(
null,
recentActivityCreator(CreatorActivityType.Live, targetId = null, creatorId = -1L)
.toHomeRecommendationRecentlyActiveCreatorRoute()
)
}
@Test
fun `홈 최근 활동 route는 알 수 없는 activity와 유효하지 않은 target을 무시한다`() {
assertEquals(
null,
recentActivityCreator(activityType = null, targetId = 10L).toHomeRecommendationRecentlyActiveCreatorRoute()
@@ -1393,9 +1429,11 @@ class HomeMainFragmentLayoutTest {
private fun recentActivityCreator(
activityType: CreatorActivityType?,
targetId: Long?
targetId: Long?,
creatorId: Long = 20L
): HomeRecommendationRecentlyActiveCreatorUiModel {
return HomeRecommendationRecentlyActiveCreatorUiModel(
creatorId = creatorId,
nickname = "크리에이터",
profileImage = "",
activityType = activityType,

View File

@@ -125,6 +125,20 @@ class HomeMainFragmentLoginGuardSourceTest {
assertTrue(source.contains("private val liveActionCoordinator: LiveActionCoordinator by lazy"))
}
@Test
fun `HomeMainFragment 최근 활동 LIVE route는 공통 Live와 Creator Action을 사용한다`() {
val source = projectFile(
"app/src/main/java/kr/co/vividnext/sodalive/v2/main/home/HomeMainFragment.kt"
).readText()
val clickSource = source.substringFrom(
"private fun onRecentActivityClick(item: HomeRecommendationRecentlyActiveCreatorUiModel)"
)
assertTrue(clickSource.contains("liveActionCoordinator.enterLiveRoom(route.roomId)"))
assertTrue(clickSource.contains("handleCreatorAction("))
assertTrue(clickSource.contains("CreatorActionCommand.Profile(route.creatorId)"))
}
@Test
fun `HomeMainFragment loading dialog는 통합 loading state로 제어한다`() {
val source = projectFile(