fix(creator): 채널 홈 활동 표시를 보정한다

This commit is contained in:
2026-06-16 21:19:03 +09:00
parent 9900ac02f5
commit 5969f50888
3 changed files with 193 additions and 50 deletions

View File

@@ -11,139 +11,176 @@
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingHorizontal="@dimen/spacing_20"
android:layout_marginTop="@dimen/spacing_14"
android:orientation="vertical">
android:orientation="vertical"
android:paddingHorizontal="@dimen/spacing_20">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/spacing_8"
android:baselineAligned="false"
android:orientation="horizontal">
<TextView
style="@style/Typography.Body2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/spacing_8"
android:layout_weight="1"
android:letterSpacing="0"
android:lineSpacingMultiplier="1.45"
android:text="@string/creator_channel_activity_debut"
android:textColor="@color/gray_500"
android:textSize="16sp" />
android:textColor="#939393" />
<TextView
android:id="@+id/tv_activity_debut_value"
style="@style/Typography.Body2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:letterSpacing="0"
android:lineSpacingMultiplier="1.45"
android:textColor="@color/white"
android:textSize="16sp" />
android:includeFontPadding="true" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/spacing_8"
android:baselineAligned="false"
android:orientation="horizontal">
<TextView
style="@style/Typography.Body2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/spacing_8"
android:layout_weight="1"
android:letterSpacing="0"
android:lineSpacingMultiplier="1.45"
android:text="@string/creator_channel_activity_live_count"
android:textColor="@color/gray_500"
android:textSize="16sp" />
android:textColor="#939393" />
<TextView
android:id="@+id/tv_activity_live_count_value"
style="@style/Typography.Body2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/white"
android:textSize="16sp" />
android:letterSpacing="0"
android:lineSpacingMultiplier="1.45"
android:textColor="@color/white" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/spacing_8"
android:baselineAligned="false"
android:orientation="horizontal">
<TextView
style="@style/Typography.Body2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/spacing_8"
android:layout_weight="1"
android:letterSpacing="0"
android:lineSpacingMultiplier="1.45"
android:text="@string/creator_channel_activity_live_duration"
android:textColor="@color/gray_500"
android:textSize="16sp" />
android:textColor="#939393" />
<TextView
android:id="@+id/tv_activity_live_duration_value"
style="@style/Typography.Body2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/white"
android:textSize="16sp" />
android:letterSpacing="0"
android:lineSpacingMultiplier="1.45"
android:textColor="@color/white" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/spacing_8"
android:baselineAligned="false"
android:orientation="horizontal">
<TextView
style="@style/Typography.Body2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/spacing_8"
android:layout_weight="1"
android:letterSpacing="0"
android:lineSpacingMultiplier="1.45"
android:text="@string/creator_channel_activity_live_contributor"
android:textColor="@color/gray_500"
android:textSize="16sp" />
android:textColor="#939393" />
<TextView
android:id="@+id/tv_activity_live_contributor_value"
style="@style/Typography.Body2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/white"
android:textSize="16sp" />
android:letterSpacing="0"
android:lineSpacingMultiplier="1.45"
android:textColor="@color/white" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/spacing_8"
android:baselineAligned="false"
android:orientation="horizontal">
<TextView
style="@style/Typography.Body2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/spacing_8"
android:layout_weight="1"
android:letterSpacing="0"
android:lineSpacingMultiplier="1.45"
android:text="@string/creator_channel_activity_audio_count"
android:textColor="@color/gray_500"
android:textSize="16sp" />
android:textColor="#939393" />
<TextView
android:id="@+id/tv_activity_audio_count_value"
style="@style/Typography.Body2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/white"
android:textSize="16sp" />
android:letterSpacing="0"
android:lineSpacingMultiplier="1.45"
android:textColor="@color/white" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:baselineAligned="false"
android:orientation="horizontal">
<TextView
style="@style/Typography.Body2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/spacing_8"
android:layout_weight="1"
android:letterSpacing="0"
android:lineSpacingMultiplier="1.45"
android:text="@string/creator_channel_activity_series_count"
android:textColor="@color/gray_500"
android:textSize="16sp" />
android:textColor="#939393" />
<TextView
android:id="@+id/tv_activity_series_count_value"
style="@style/Typography.Body2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/white"
android:textSize="16sp" />
android:letterSpacing="0"
android:lineSpacingMultiplier="1.45"
android:textColor="@color/white" />
</LinearLayout>
</LinearLayout>
</LinearLayout>

View File

@@ -10,6 +10,7 @@ import kr.co.vividnext.sodalive.v2.creator.channel.ui.calculateCreatorChannelFan
import kr.co.vividnext.sodalive.v2.creator.channel.ui.calculateCreatorChannelSeriesCardHeightDp
import kr.co.vividnext.sodalive.v2.creator.channel.ui.calculateCreatorChannelSeriesCardWidthDp
import kr.co.vividnext.sodalive.v2.creator.channel.ui.calculateCreatorChannelScheduleTimelineLineCount
import kr.co.vividnext.sodalive.v2.creator.channel.ui.formatCreatorChannelDebutActivityValue
import kr.co.vividnext.sodalive.v2.creator.channel.ui.formatCreatorChannelScheduleDate
import kr.co.vividnext.sodalive.v2.creator.channel.ui.formatCreatorChannelScheduleDayOfWeek
import kr.co.vividnext.sodalive.v2.creator.channel.ui.formatCreatorChannelScheduleTime
@@ -403,18 +404,20 @@ class CreatorChannelActivitySourceTest {
"app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/ui/CreatorChannelHomeSectionAdapter.kt"
).readText()
assertTrue(adapter.contains("creator_channel_activity_debut"))
assertTrue(adapter.contains("creator_channel_activity_debut_format"))
val activityLayout = projectFile("app/src/main/res/layout/item_creator_channel_home_activity.xml").readText()
assertTrue(activityLayout.contains("creator_channel_activity_debut"))
assertTrue(adapter.contains("formatCreatorChannelDebutActivityValue"))
assertTrue(adapter.contains("creator_channel_activity_live_count_format"))
assertTrue(adapter.contains("creator_channel_activity_live_duration_format"))
assertTrue(adapter.contains("creator_channel_activity_live_contributor_format"))
assertTrue(adapter.contains("creator_channel_activity_audio_count_format"))
assertTrue(adapter.contains("creator_channel_activity_series_count_format"))
assertTrue(adapter.contains("creator_channel_activity_live_count"))
assertTrue(adapter.contains("creator_channel_activity_live_duration"))
assertTrue(adapter.contains("creator_channel_activity_live_contributor"))
assertTrue(adapter.contains("creator_channel_activity_audio_count"))
assertTrue(adapter.contains("creator_channel_activity_series_count"))
assertTrue(activityLayout.contains("creator_channel_activity_live_count"))
assertTrue(activityLayout.contains("creator_channel_activity_live_duration"))
assertTrue(activityLayout.contains("creator_channel_activity_live_contributor"))
assertTrue(activityLayout.contains("creator_channel_activity_audio_count"))
assertTrue(activityLayout.contains("creator_channel_activity_series_count"))
assertFalse(adapter.contains("addActivityRow(activity.dDay, activity.debutDateUtc.orEmpty())"))
assertFalse(adapter.contains("\"Live "))
assertFalse(adapter.contains("\"Audio "))
@@ -496,7 +499,7 @@ class CreatorChannelActivitySourceTest {
assertFalse(activityLayout.contains("creator_channel_activity_photo"))
assertFalse(activityLayout.contains("bg_round_corner_16_7_222222"))
assertTrue(adapter.contains("private val activityDebutValue: TextView?"))
assertTrue(adapter.contains("activityDebutValue?.text = formatDebutActivityValue"))
assertTrue(adapter.contains("activityDebutValue?.text = formatCreatorChannelDebutActivityValue"))
assertTrue(adapter.contains("activitySeriesCountValue?.text = itemView.context.getString"))
assertFalse(adapter.contains("private fun addActivityRow"))
assertFalse(adapter.contains("addActivityRow("))
@@ -656,7 +659,12 @@ class CreatorChannelActivitySourceTest {
assertTrue(adapter.contains("private val donationEmpty: View?"))
assertTrue(adapter.contains("donationItemsScrollView?.isVisible = item.donations.isNotEmpty()"))
assertTrue(adapter.contains("donationEmpty?.isVisible = item.donations.isEmpty()"))
assertTrue(adapter.contains("private fun bindDonations(item: CreatorChannelHomeSection.Donations) {\n donationItems?.removeAllViews()"))
assertTrue(
adapter.contains(
"private fun bindDonations(item: CreatorChannelHomeSection.Donations) {\n" +
" donationItems?.removeAllViews()"
)
)
assertTrue(adapter.contains("R.layout.item_creator_channel_home_donation_row"))
assertTrue(adapter.contains("donationItems?.addView(row)"))
assertTrue(adapter.contains("val visibleDonations = item.donations.take(MAX_DONATION_ITEM_COUNT)"))
@@ -687,25 +695,25 @@ class CreatorChannelActivitySourceTest {
assertTrue(adapter.contains("val isDonationEmptyButtonVisible = !item.isOwner"))
assertTrue(adapter.contains("donationButton?.isVisible = isDonationButtonVisible"))
assertTrue(adapter.contains("donationEmptyButton?.isVisible = isDonationEmptyButtonVisible"))
assertTrue(
adapter.contains(
"donationButton?.setOnClickListener(if (isDonationButtonVisible) View.OnClickListener { onDonationClick() } else null)"
)
)
assertTrue(
adapter.contains(
"donationEmptyButton?.setOnClickListener(if (isDonationEmptyButtonVisible) View.OnClickListener { onDonationClick() } else null)"
)
)
assertTrue(adapter.contains("donationButton?.setOnClickListener("))
assertTrue(adapter.contains("if (isDonationButtonVisible) View.OnClickListener { onDonationClick() } else null"))
assertTrue(adapter.contains("donationEmptyButton?.setOnClickListener("))
assertTrue(adapter.contains("if (isDonationEmptyButtonVisible) View.OnClickListener { onDonationClick() } else null"))
assertTrue(fragment.contains("override fun postChannelDonation(can: Int, isSecret: Boolean, message: String)"))
assertTrue(fragment.contains("viewModel.postChannelDonation(can = can, isSecret = isSecret, message = message)"))
assertTrue(
fragment.contains("viewModel.postChannelDonation(can = can, isSecret = isSecret, message = message)")
)
assertTrue(fragment.contains("host.onCreatorChannelDonationClicked()"))
assertTrue(activity.contains("LiveRoomDonationDialog"))
assertTrue(activity.contains("isLiveDonation = true"))
assertTrue(activity.contains("messageMaxLength = 100"))
assertTrue(activity.contains("secretToggleLabelResId = R.string.screen_user_profile_channel_donation_secret"))
assertTrue(
activity.contains("secretToggleLabelResId = R.string.screen_user_profile_channel_donation_secret")
)
assertTrue(activity.contains("applySecretMissionMessageHint = false"))
assertTrue(activity.contains("homeActionDelegate?.postChannelDonation(can = can, isSecret = isSecret, message = message)"))
assertTrue(
activity.contains("homeActionDelegate?.postChannelDonation(can = can, isSecret = isSecret, message = message)")
)
assertTrue(activity.contains("dialog.show(screenWidth - 26.7f.dpToPx().toInt())"))
}
@@ -992,6 +1000,30 @@ class CreatorChannelActivitySourceTest {
assertTrue(source.contains("putExtra(Constants.EXTRA_AUDIO_CONTENT_ID, audioContent.audioContentId)"))
}
@Test
fun `Phase 12 오디오 컨텐츠는 표시 개수만큼 높이를 차지하고 free tag는 wrap content를 사용한다`() {
val adapter = projectFile(
"app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/ui/CreatorChannelHomeSectionAdapter.kt"
).readText()
val audioItemLayout = projectFile(
"app/src/main/res/layout/item_creator_channel_home_audio_content.xml"
).readText()
assertTrue(audioItemLayout.contains("android:id=\"@+id/tv_audio_content_free_tag\""))
assertTrue(audioItemLayout.contains("android:layout_width=\"wrap_content\""))
assertFalse(
audioItemLayout.contains(
"android:id=\"@+id/tv_audio_content_free_tag\"\n" +
" style=\"@style/Typography.Body6\"\n" +
" android:layout_width=\"34dp\""
)
)
assertTrue(adapter.contains("private fun updateAudioContentsGridSpan(itemCount: Int)"))
assertTrue(adapter.contains("spanCount = itemCount.coerceIn("))
assertTrue(adapter.contains("AUDIO_GRID_SPAN_COUNT"))
assertTrue(adapter.contains("updateAudioContentsGridSpan(visibleAudioContents.size)"))
}
@Test
fun `시리즈 섹션은 Figma Contents series size m 카드 row로 렌더링한다`() {
val adapter = projectFile(
@@ -1018,7 +1050,7 @@ class CreatorChannelActivitySourceTest {
assertTrue(seriesItemLayout.contains("@+id/tv_series_original_text"))
assertTrue(seriesItemLayout.contains("android:text=\"Only\""))
assertTrue(seriesItemLayout.contains("@font/phosphate_solid"))
assertTrue(seriesItemLayout.contains("android:layout_width=\"70dp\""))
assertTrue(seriesItemLayout.contains("android:layout_width=\"wrap_content\""))
assertTrue(seriesItemLayout.contains("@drawable/bg_series_original_tag"))
assertTrue(seriesItemLayout.contains("@drawable/ic_series_original"))
assertTrue(seriesItemLayout.contains("android:layout_width=\"163dp\""))
@@ -1079,6 +1111,72 @@ class CreatorChannelActivitySourceTest {
assertEquals(206, calculateCreatorChannelSeriesCardHeightDp(146))
}
@Test
fun `Phase 12 시리즈 original tag는 wrap content sizing과 상세 이동 계약을 사용한다`() {
val source = projectFile(
"app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt"
).readText()
val fragment = projectFile(
"app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelHomeFragment.kt"
).readText()
val seriesItemLayout = projectFile(
"app/src/main/res/layout/item_creator_channel_home_series_content.xml"
).readText()
assertTrue(seriesItemLayout.contains("android:id=\"@+id/layout_series_original_tag\""))
assertTrue(seriesItemLayout.contains("android:layout_width=\"wrap_content\""))
assertTrue(seriesItemLayout.contains("android:layout_height=\"wrap_content\""))
assertTrue(seriesItemLayout.contains("android:paddingStart=\"8dp\""))
assertTrue(seriesItemLayout.contains("android:paddingEnd=\"8dp\""))
assertTrue(seriesItemLayout.contains("android:minHeight=\"24dp\""))
assertFalse(seriesItemLayout.contains("android:layout_width=\"70dp\""))
assertTrue(fragment.contains("onSeriesClick = ::onSeriesClicked"))
assertTrue(fragment.contains("private fun onSeriesClicked(series: CreatorChannelSeriesResponse)"))
assertTrue(fragment.contains("host.onCreatorChannelSeriesClicked(series)"))
assertTrue(source.contains("import kr.co.vividnext.sodalive.audio_content.series.detail.SeriesDetailActivity"))
assertTrue(source.contains("private fun onSeriesClicked(series: CreatorChannelSeriesResponse)"))
assertTrue(source.contains("SeriesDetailActivity::class.java"))
assertTrue(source.contains("putExtra(Constants.EXTRA_SERIES_ID, series.seriesId)"))
}
@Test
fun `Phase 12 활동 영역은 Figma label value typography와 debut 날짜 포맷을 사용한다`() {
val adapter = projectFile(
"app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/ui/CreatorChannelHomeSectionAdapter.kt"
).readText()
val activityLayout = projectFile("app/src/main/res/layout/item_creator_channel_home_activity.xml").readText()
assertTrue(activityLayout.contains("@layout/view_section_title"))
assertTrue(activityLayout.contains("style=\"@style/Typography.Body2\""))
assertTrue(activityLayout.contains("android:lineSpacingMultiplier=\"1.45\""))
assertTrue(activityLayout.contains("android:letterSpacing=\"0\""))
assertTrue(activityLayout.contains("android:textColor=\"#939393\""))
assertTrue(activityLayout.contains("android:textColor=\"@color/white\""))
assertTrue(activityLayout.contains("android:layout_marginEnd=\"@dimen/spacing_8\""))
assertTrue(activityLayout.contains("android:layout_marginBottom=\"@dimen/spacing_8\""))
assertTrue(adapter.contains("formatCreatorChannelDebutActivityValue("))
assertFalse(
adapter.contains("itemView.context.getString(R.string.creator_channel_activity_debut_format, debutDate, dDay)")
)
assertEquals(
"2026.06.11(D+1)",
formatCreatorChannelDebutActivityValue(
"2026-06-10T15:00:00Z",
"D+1",
TimeZone.getTimeZone("Asia/Seoul")
)
)
assertEquals(
"D+1",
formatCreatorChannelDebutActivityValue(null, "D+1", TimeZone.getTimeZone("Asia/Seoul"))
)
assertEquals(
"D+1",
formatCreatorChannelDebutActivityValue("invalid", "D+1", TimeZone.getTimeZone("Asia/Seoul"))
)
}
@Test
fun `커뮤니티 섹션은 Figma feed card 3개와 전체보기 capsule로 렌더링한다`() {
val adapter = projectFile(